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/Deferred.js | |
| 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/Deferred.js')
| -rw-r--r-- | src/Deferred.js | 206 |
1 files changed, 206 insertions, 0 deletions
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); + }]; +} |
