diff options
| author | Igor Minar | 2011-11-08 01:56:42 -0800 |
|---|---|---|
| committer | Igor Minar | 2011-11-30 14:49:03 -0500 |
| commit | 1cdfa3b9601c199ec0b45096b38e26350eca744f (patch) | |
| tree | c6b87d6a8cdbcd7e65a1bbad304df25e993b8568 /src | |
| parent | 16363d8000a484b428a16eb07d8bd0f19c4b4337 (diff) | |
| download | angular.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.js | 1 | ||||
| -rw-r--r-- | src/Deferred.js | 206 | ||||
| -rw-r--r-- | src/service/http.js | 2 | ||||
| -rw-r--r-- | src/service/scope.js | 4 |
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); }, /** |
