aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorIgor Minar2011-11-08 01:56:42 -0800
committerIgor Minar2011-11-30 14:49:03 -0500
commit1cdfa3b9601c199ec0b45096b38e26350eca744f (patch)
treec6b87d6a8cdbcd7e65a1bbad304df25e993b8568 /src
parent16363d8000a484b428a16eb07d8bd0f19c4b4337 (diff)
downloadangular.js-1cdfa3b9601c199ec0b45096b38e26350eca744f.tar.bz2
feat(deferreds/promises): Q-like deferred/promise implementation with a ton of specs
Diffstat (limited to 'src')
-rw-r--r--src/AngularPublic.js1
-rw-r--r--src/Deferred.js206
-rw-r--r--src/service/http.js2
-rw-r--r--src/service/scope.js4
4 files changed, 210 insertions, 3 deletions
diff --git a/src/AngularPublic.js b/src/AngularPublic.js
index 105059dc..7839ea63 100644
--- a/src/AngularPublic.js
+++ b/src/AngularPublic.js
@@ -86,6 +86,7 @@ function ngModule($provide, $injector) {
$provide.service('$route', $RouteProvider);
$provide.service('$routeParams', $RouteParamsProvider);
$provide.service('$rootScope', $RootScopeProvider);
+ $provide.service('$q', $QProvider);
$provide.service('$sniffer', $SnifferProvider);
$provide.service('$templateCache', $TemplateCacheProvider);
$provide.service('$window', $WindowProvider);
diff --git a/src/Deferred.js b/src/Deferred.js
new file mode 100644
index 00000000..bf20d413
--- /dev/null
+++ b/src/Deferred.js
@@ -0,0 +1,206 @@
+'use strict';
+
+/**
+ * inspired by Kris Kowal's Q (https://github.com/kriskowal/q)
+ */
+
+/**
+ * Constructs a promise manager.
+ *
+ * @param {function(function)=} nextTick Function for executing functions in the next turn. Falls
+ * back to `setTimeout` if undefined.
+ * @param {function(...*)=} exceptionHandler Function into which unexpected exceptions are passed for
+ * debugging purposes. Falls back to `console.error` if undefined,
+ * @returns {object} Promise manager.
+ */
+function qFactory(nextTick, exceptionHandler) {
+
+ nextTick = nextTick || function(callback) {
+ setTimeout(callback, 0); // very rare since most of queueing will be handled within $apply
+ };
+
+ exceptionHandler = exceptionHandler || function(e) {
+ // TODO(i): console.error is somehow reset to function(a) {}, it might be a JSTD bug
+ if (console && console.log) {
+ console.log(e);
+ }
+ }
+
+ var defer = function() {
+ var pending = [],
+ value, deferred;
+
+ deferred = {
+
+ resolve: function(val) {
+ if (pending) {
+ var callbacks = pending;
+ pending = undefined;
+ value = ref(val);
+
+ if (callbacks.length) {
+ nextTick(function() {
+ var callback;
+ for (var i = 0, ii = callbacks.length; i < ii; i++) {
+ callback = callbacks[i];
+ value.then(callback[0], callback[1]);
+ }
+ });
+ }
+ }
+ },
+
+
+ reject: function(reason) {
+ deferred.resolve(reject(reason));
+ },
+
+
+ promise: {
+ then: function(callback, errback) {
+ var result = defer();
+
+ var wrappedCallback = function(value) {
+ try {
+ result.resolve((callback || defaultCallback)(value));
+ } catch(e) {
+ exceptionHandler(e);
+ result.reject(e);
+ }
+ };
+
+ var wrappedErrback = function(reason) {
+ try {
+ result.resolve((errback || defaultErrback)(reason));
+ } catch(e) {
+ exceptionHandler(e);
+ result.reject(e);
+ }
+ };
+
+ if (pending) {
+ pending.push([wrappedCallback, wrappedErrback]);
+ } else {
+ value.then(wrappedCallback, wrappedErrback);
+ }
+
+ return result.promise;
+ }
+ }
+ };
+
+ return deferred;
+ };
+
+
+ var ref = function(value) {
+ if (value && value.then) return value;
+ return {
+ then: function(callback) {
+ var result = defer();
+ nextTick(function() {
+ result.resolve(callback(value));
+ });
+ return result.promise;
+ }
+ };
+ };
+
+
+ var reject = function(reason) {
+ return {
+ then: function(callback, errback) {
+ var result = defer();
+ nextTick(function() {
+ result.resolve(errback(reason));
+ });
+ return result.promise;
+ }
+ };
+ };
+
+
+ var when = function(value, callback, errback) {
+ var result = defer(),
+ done;
+
+ var wrappedCallback = function(value) {
+ try {
+ return (callback || defaultCallback)(value);
+ } catch (e) {
+ exceptionHandler(e);
+ return reject(e);
+ }
+ };
+
+ var wrappedErrback = function(reason) {
+ try {
+ return (errback || defaultErrback)(reason);
+ } catch (e) {
+ exceptionHandler(e);
+ return reject(e);
+ }
+ };
+
+ nextTick(function() {
+ ref(value).then(function(value) {
+ if (done) return;
+ done = true;
+ result.resolve(ref(value).then(wrappedCallback, wrappedErrback));
+ }, function(reason) {
+ if (done) return;
+ done = true;
+ result.resolve(wrappedErrback(reason));
+ });
+ });
+
+ return result.promise;
+ };
+
+
+ function defaultCallback(value) {
+ return value;
+ }
+
+
+ function defaultErrback(reason) {
+ return reject(reason);
+ }
+
+
+ function all(promises) {
+ var deferred = defer(),
+ counter = promises.length,
+ results = [];
+
+ forEach(promises, function(promise, index) {
+ promise.then(function(value) {
+ if (index in results) return;
+ results[index] = value;
+ if (!(--counter)) deferred.resolve(results);
+ }, function(reason) {
+ if (index in results) return;
+ deferred.reject(reason);
+ });
+ });
+
+ return deferred.promise;
+ }
+
+ return {
+ defer: defer,
+ reject: reject,
+ when: when,
+ all: all
+ };
+}
+
+// TODO(i): move elsewhere
+function $QProvider() {
+
+ this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
+ return qFactory(function(callback) {
+ $rootScope.$evalAsync(callback);
+ }, $exceptionHandler);
+ }];
+}
diff --git a/src/service/http.js b/src/service/http.js
index 6efe7474..8458617b 100644
--- a/src/service/http.js
+++ b/src/service/http.js
@@ -229,7 +229,7 @@ function $HttpProvider() {
/**
* Represents Request object, returned by $http()
*
- * !!! ACCESS CLOSURE VARS:
+ * !!! ACCESSES CLOSURE VARS:
* $httpBackend, $browser, $config, $log, $rootScope, defaultCache, $http.pendingRequests
*/
function XhrFuture() {
diff --git a/src/service/scope.js b/src/service/scope.js
index cbda4495..d6cf60c9 100644
--- a/src/service/scope.js
+++ b/src/service/scope.js
@@ -381,11 +381,11 @@ function $RootScopeProvider(){
}
} while ((current = next));
- if(!(ttl--)) {
+ if(dirty && !(ttl--)) {
throw Error('100 $digest() iterations reached. Aborting!\n' +
'Watchers fired in the last 5 iterations: ' + toJson(watchLog));
}
- } while (dirty);
+ } while (dirty || asyncQueue.length);
},
/**