123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730 |
- (function(define){
- define(function(require,exports){
- // Kris Zyp
- // this is based on the CommonJS spec for promises:
- // http://wiki.commonjs.org/wiki/Promises
- // Includes convenience functions for promises, much of this is taken from Tyler Close's ref_send
- // and Kris Kowal's work on promises.
- // // MIT License
- // A typical usage:
- // A default Promise constructor can be used to create a self-resolving deferred/promise:
- // var Promise = require("promise").Promise;
- // var promise = new Promise();
- // asyncOperation(function(){
- // Promise.resolve("succesful result");
- // });
- // promise -> given to the consumer
- //
- // A consumer can use the promise
- // promise.then(function(result){
- // ... when the action is complete this is executed ...
- // },
- // function(error){
- // ... executed when the promise fails
- // });
- //
- // Alternately, a provider can create a deferred and resolve it when it completes an action.
- // The deferred object a promise object that provides a separation of consumer and producer to protect
- // promises from being fulfilled by untrusted code.
- // var defer = require("promise").defer;
- // var deferred = defer();
- // asyncOperation(function(){
- // deferred.resolve("succesful result");
- // });
- // deferred.promise -> given to the consumer
- //
- // Another way that a consumer can use the promise (using promise.then is also allowed)
- // var when = require("promise").when;
- // when(promise,function(result){
- // ... when the action is complete this is executed ...
- // },
- // function(error){
- // ... executed when the promise fails
- // });
- exports.errorTimeout = 100;
- var freeze = Object.freeze || function(){};
- /**
- * Default constructor that creates a self-resolving Promise. Not all promise implementations
- * need to use this constructor.
- */
- var Promise = function(canceller){
- };
- /**
- * Promise implementations must provide a "then" function.
- */
- Promise.prototype.then = function(resolvedCallback, errorCallback, progressCallback){
- throw new TypeError("The Promise base class is abstract, this function must be implemented by the Promise implementation");
- };
- /**
- * If an implementation of a promise supports a concurrency model that allows
- * execution to block until the promise is resolved, the wait function may be
- * added.
- */
- /**
- * If an implementation of a promise can be cancelled, it may add this function
- */
- // Promise.prototype.cancel = function(){
- // };
- Promise.prototype.get = function(propertyName){
- return this.then(function(value){
- return value[propertyName];
- });
- };
- Promise.prototype.put = function(propertyName, value){
- return this.then(function(object){
- return object[propertyName] = value;
- });
- };
- Promise.prototype.call = function(functionName /*, args */){
- var fnArgs = Array.prototype.slice.call(arguments, 1);
- return this.then(function(value){
- return value[functionName].apply(value, fnArgs);
- });
- };
- /**
- * This can be used to conviently resolve a promise with auto-handling of errors:
- * setTimeout(deferred.resolverCallback(function(){
- * return doSomething();
- * }), 100);
- */
- Promise.prototype.resolverCallback = function(callback){
- var self = this;
- return function(){
- try{
- self.resolve(callback());
- }catch(e){
- self.reject(e);
- }
- }
- };
- /** Dojo/NodeJS methods*/
- Promise.prototype.addCallback = function(callback){
- return this.then(callback);
- };
- Promise.prototype.addErrback = function(errback){
- return this.then(function(){}, errback);
- };
- /*Dojo methods*/
- Promise.prototype.addBoth = function(callback){
- return this.then(callback, callback);
- };
- Promise.prototype.addCallbacks = function(callback, errback){
- return this.then(callback, errback);
- };
- /*NodeJS method*/
- Promise.prototype.wait = function(){
- return exports.wait(this);
- };
- Deferred.prototype = Promise.prototype;
- // A deferred provides an API for creating and resolving a promise.
- exports.Promise = exports.Deferred = exports.defer = defer;
- function defer(canceller){
- return new Deferred(canceller);
- }
- // currentContext can be set to other values
- // and mirrors the global. We need to go off the global in case of multiple instances
- // of this module, which isn't rare with NPM's package policy.
- Object.defineProperty && Object.defineProperty(exports, "currentContext", {
- set: function(value){
- currentContext = value;
- },
- get: function(){
- return currentContext;
- }
- });
- exports.currentContext = null;
- function Deferred(canceller){
- var result, finished, isError, waiting = [], handled;
- var promise = this.promise = new Promise();
- var context = exports.currentContext;
- function notifyAll(value){
- var previousContext = exports.currentContext;
- if(finished){
- throw new Error("This deferred has already been resolved");
- }
- try{
- if(previousContext !== context){
- if(previousContext && previousContext.suspend){
- previousContext.suspend();
- }
- exports.currentContext = context;
- if(context && context.resume){
- context.resume();
- }
- }
- result = value;
- finished = true;
- for(var i = 0; i < waiting.length; i++){
- notify(waiting[i]);
- }
- }
- finally{
- if(previousContext !== context){
- if(context && context.suspend){
- context.suspend();
- }
- if(previousContext && previousContext.resume){
- previousContext.resume();
- }
- exports.currentContext = previousContext;
- }
- }
- }
- function notify(listener){
- var func = (isError ? listener.error : listener.resolved);
- if(func){
- handled ?
- (handled.handled = true) : (handled = true);
- try{
- var newResult = func(result);
- if(newResult && typeof newResult.then === "function"){
- newResult.then(listener.deferred.resolve, listener.deferred.reject);
- return;
- }
- listener.deferred.resolve(newResult);
- }
- catch(e){
- listener.deferred.reject(e);
- }
- }
- else{
- if(isError){
- listener.deferred.reject(result, typeof handled === "object" ? handled : (handled = {}));
- }
- else{
- listener.deferred.resolve.call(listener.deferred, result);
- }
- }
- }
- // calling resolve will resolve the promise
- this.resolve = this.callback = this.emitSuccess = function(value){
- notifyAll(value);
- };
- // calling error will indicate that the promise failed
- var reject = this.reject = this.errback = this.emitError = function(error, handledObject){
- if (typeof handledObject == "object") {
- if (handled) {
- handledObject.handled = true;
- } else {
- handled = handledObject;
- }
- }
- isError = true;
- notifyAll(error);
- if (!handledObject && typeof setTimeout !== "undefined") {
- if (!(typeof handled == "object" ? handled.handled : handled)) {
- // set the time out if it has not already been handled
- setTimeout(function () {
- if (!(typeof handled == "object" ? handled.handled : handled)) {
- throw error;
- }
- }, exports.errorTimeout);
- }
- }
- return handled;
- };
- // call progress to provide updates on the progress on the completion of the promise
- this.progress = function(update){
- for(var i = 0; i < waiting.length; i++){
- var progress = waiting[i].progress;
- progress && progress(update);
- }
- }
- // provide the implementation of the promise
- this.then = promise.then = function(resolvedCallback, errorCallback, progressCallback){
- var returnDeferred = new Deferred(promise.cancel);
- var listener = {resolved: resolvedCallback, error: errorCallback, progress: progressCallback, deferred: returnDeferred};
- if(finished){
- notify(listener);
- }
- else{
- waiting.push(listener);
- }
- return returnDeferred.promise;
- };
- var timeout;
- if(typeof setTimeout !== "undefined") {
- this.timeout = function (ms) {
- if (ms === undefined) {
- return timeout;
- }
- timeout = ms;
- setTimeout(function () {
- if (!finished) {
- if (promise.cancel) {
- promise.cancel(new Error("timeout"));
- }
- else {
- reject(new Error("timeout"));
- }
- }
- }, ms);
- return promise;
- };
- }
- if(canceller){
- this.cancel = promise.cancel = function(){
- var error = canceller();
- if(!(error instanceof Error)){
- error = new Error(error);
- }
- reject(error);
- }
- }
- freeze(promise);
- };
- function perform(value, async, sync){
- try{
- if(value && typeof value.then === "function"){
- value = async(value);
- }
- else{
- value = sync(value);
- }
- if(value && typeof value.then === "function"){
- return value;
- }
- var deferred = new Deferred();
- deferred.resolve(value);
- return deferred.promise;
- }catch(e){
- var deferred = new Deferred();
- deferred.reject(e);
- return deferred.promise;
- }
- }
- /**
- * Promise manager to make it easier to consume promises
- */
- function rethrow(err){ throw err; }
- /**
- * Registers an observer on a promise, always returning a promise
- * @param value promise or value to observe
- * @param resolvedCallback function to be called with the resolved value
- * @param rejectCallback function to be called with the rejection reason
- * @param progressCallback function to be called when progress is made
- * @return promise for the return value from the invoked callback
- */
- exports.whenPromise = function(value, resolvedCallback, rejectCallback, progressCallback){
- var deferred = defer();
- if(value && typeof value.then === "function"){
- value.then(function(next){
- deferred.resolve(next);
- },function(error){
- deferred.reject(error);
- });
- rejectCallback = rejectCallback || rethrow;
- }else{
- deferred.resolve(value);
- }
- return deferred.promise.then(resolvedCallback, rejectCallback, progressCallback);
- };
- /**
- * Registers an observer on a promise.
- * @param value promise or value to observe
- * @param resolvedCallback function to be called with the resolved value
- * @param rejectCallback function to be called with the rejection reason
- * @param progressCallback function to be called when progress is made
- * @return promise for the return value from the invoked callback or the value if it
- * is a non-promise value
- */
- exports.when = function(value, resolvedCallback, rejectCallback, progressCallback){
- if(value && typeof value.then === "function"){
- if(value instanceof Promise){
- return value.then(resolvedCallback, rejectCallback, progressCallback);
- }
- else{
- return exports.whenPromise(value, resolvedCallback, rejectCallback, progressCallback);
- }
- }
- return resolvedCallback ? resolvedCallback(value) : value;
- };
- /**
- * This is convenience function for catching synchronously and asynchronously thrown
- * errors. This is used like when() except you execute the initial action in a callback:
- * whenCall(function(){
- * return doSomethingThatMayReturnAPromise();
- * }, successHandler, errorHandler);
- */
- exports.whenCall = function(initialCallback, resolvedCallback, rejectCallback, progressCallback){
- try{
- return exports.when(initialCallback(), resolvedCallback, rejectCallback, progressCallback);
- }catch(e){
- return rejectCallback(e);
- }
- }
- /**
- * Gets the value of a property in a future turn.
- * @param target promise or value for target object
- * @param property name of property to get
- * @return promise for the property value
- */
- exports.get = function(target, property){
- return perform(target, function(target){
- return target.get(property);
- },
- function(target){
- return target[property]
- });
- };
- /**
- * Invokes a method in a future turn.
- * @param target promise or value for target object
- * @param methodName name of method to invoke
- * @param args array of invocation arguments
- * @return promise for the return value
- */
- exports.call = function(target, methodName, args){
- return perform(target, function(target){
- return target.call(methodName, args);
- },
- function(target){
- return target[methodName].apply(target, args);
- });
- };
- /**
- * Sets the value of a property in a future turn.
- * @param target promise or value for target object
- * @param property name of property to set
- * @param value new value of property
- * @return promise for the return value
- */
- exports.put = function(target, property, value){
- return perform(target, function(target){
- return target.put(property, value);
- },
- function(target){
- return target[property] = value;
- });
- };
- /**
- * Waits for the given promise to finish, blocking (and executing other events)
- * if necessary to wait for the promise to finish. If target is not a promise
- * it will return the target immediately. If the promise results in an reject,
- * that reject will be thrown.
- * @param target promise or value to wait for.
- * @return the value of the promise;
- */
- var queue;
- //try {
- // queue = require("event-loop");
- //}
- //catch (e) {}
- exports.wait = function(target){
- if(!queue){
- throw new Error("Can not wait, the event-queue module is not available");
- }
- if(target && typeof target.then === "function"){
- var isFinished, isError, result;
- target.then(function(value){
- isFinished = true;
- result = value;
- },
- function(error){
- isFinished = true;
- isError = true;
- result = error;
- });
- while(!isFinished){
- queue.processNextEvent(true);
- }
- if(isError){
- throw result;
- }
- return result;
- }
- else{
- return target;
- }
- };
- /**
- * Takes an array of promises and returns a promise that is fulfilled once all
- * the promises in the array are fulfilled
- * @param array The array of promises
- * @return the promise that is fulfilled when all the array is fulfilled, resolved to the array of results
- */
- exports.all = function(array){
- var deferred = new Deferred();
- if(Object.prototype.toString.call(array) !== '[object Array]'){
- array = Array.prototype.slice.call(arguments);
- }
- var fulfilled = 0, length = array.length, rejected = false;
- var results = [];
- if (length === 0) deferred.resolve(results);
- else {
- array.forEach(function(promise, index){
- exports.when(promise,
- function(value){
- results[index] = value;
- fulfilled++;
- if(fulfilled === length){
- deferred.resolve(results);
- }
- },
- function(error){
- if(!rejected){
- deferred.reject(error);
- }
- rejected = true;
- });
- });
- }
- return deferred.promise;
- };
- /**
- * Takes a hash of promises and returns a promise that is fulfilled once all
- * the promises in the hash keys are fulfilled
- * @param hash The hash of promises
- * @return the promise that is fulfilled when all the hash keys is fulfilled, resolved to the hash of results
- */
- exports.allKeys = function(hash){
- var deferred = new Deferred();
- var array = Object.keys(hash);
- var fulfilled = 0, length = array.length;
- var results = {};
- if (length === 0) deferred.resolve(results);
- else {
- array.forEach(function(key){
- exports.when(hash[key],
- function(value){
- results[key] = value;
- fulfilled++;
- if(fulfilled === length){
- deferred.resolve(results);
- }
- },
- deferred.reject);
- });
- }
- return deferred.promise;
- };
- /**
- * Takes an array of promises and returns a promise that is fulfilled when the first
- * promise in the array of promises is fulfilled
- * @param array The array of promises
- * @return a promise that is fulfilled with the value of the value of first promise to be fulfilled
- */
- exports.first = function(array){
- var deferred = new Deferred();
- if(Object.prototype.toString.call(array) !== '[object Array]'){
- array = Array.prototype.slice.call(arguments);
- }
- var fulfilled;
- array.forEach(function(promise, index){
- exports.when(promise, function(value){
- if (!fulfilled) {
- fulfilled = true;
- deferred.resolve(value);
- }
- },
- function(error){
- if (!fulfilled) {
- fulfilled = true;
- deferred.resolve(error);
- }
- });
- });
- return deferred.promise;
- };
- /**
- * Takes an array of asynchronous functions (that return promises) and
- * executes them sequentially. Each funtion is called with the return value of the last function
- * @param array The array of function
- * @param startingValue The value to pass to the first function
- * @return the value returned from the last function
- */
- exports.seq = function(array, startingValue){
- array = array.concat(); // make a copy
- var deferred = new Deferred();
- function next(value){
- var nextAction = array.shift();
- if(nextAction){
- exports.when(nextAction(value), next, function(error){
- deferred.reject(error, true);
- });
- }
- else {
- deferred.resolve(value);
- }
- }
- next(startingValue);
- return deferred.promise;
- };
- /**
- * Delays for a given amount of time and then fulfills the returned promise.
- * @param milliseconds The number of milliseconds to delay
- * @return A promise that will be fulfilled after the delay
- */
- if(typeof setTimeout !== "undefined") {
- exports.delay = function(milliseconds) {
- var deferred = new Deferred();
- setTimeout(function(){
- deferred.resolve();
- }, milliseconds);
- return deferred.promise;
- };
- }
- /**
- * Runs a function that takes a callback, but returns a Promise instead.
- * @param func node compatible async function which takes a callback as its last argument
- * @return promise for the return value from the callback from the function
- */
- exports.execute = function(asyncFunction){
- var args = Array.prototype.slice.call(arguments, 1);
- var deferred = new Deferred();
- args.push(function(error, result){
- if(error) {
- deferred.emitError(error);
- }
- else {
- if(arguments.length > 2){
- // if there are multiple success values, we return an array
- Array.prototype.shift.call(arguments, 1);
- deferred.emitSuccess(arguments);
- }
- else{
- deferred.emitSuccess(result);
- }
- }
- });
- asyncFunction.apply(this, args);
- return deferred.promise;
- };
- function isGeneratorFunction(obj){
- return obj && obj.constructor && 'GeneratorFunction' == obj.constructor.name;
- }
- /**
- * Promise-based coroutine trampoline
- * Adapted from https://github.com/deanlandolt/copromise/blob/master/copromise.js
- */
- function run(coroutine){
- var deferred = defer();
- (function next(value, exception) {
- var result;
- try {
- result = exception ? coroutine.throw(value) : coroutine.next(value);
- }
- catch (error) {
- return deferred.reject(error);
- }
- if (result.done) return deferred.resolve(result.value);
- exports.when(result.value, next, function(error) {
- next(error, true);
- });
- })();
- return deferred.promise;
- };
- /**
- * Creates a task from a coroutine, provided as generator. The `yield` function can be provided
- * a promise (or any value) to wait on, and the value will be provided when the promise resolves.
- * @param coroutine generator or generator function to treat as a coroutine
- * @return promise for the return value from the coroutine
- */
- exports.spawn = function(coroutine){
- if (isGeneratorFunction(coroutine)) {
- coroutine = coroutine();
- }
- return run(coroutine);
- }
- /**
- * Converts a Node async function to a promise returning function
- * @param func node compatible async function which takes a callback as its last argument
- * @return A function that returns a promise
- */
- exports.convertNodeAsyncFunction = function(asyncFunction, callbackNotDeclared){
- var arity = asyncFunction.length;
- return function(){
- var deferred = new Deferred();
- if(callbackNotDeclared){
- arity = arguments.length + 1;
- }
- arguments.length = arity;
- arguments[arity - 1] = function(error, result){
- if(error) {
- deferred.emitError(error);
- }
- else {
- if(arguments.length > 2){
- // if there are multiple success values, we return an array
- Array.prototype.shift.call(arguments, 1);
- deferred.emitSuccess(arguments);
- }
- else{
- deferred.emitSuccess(result);
- }
- }
- };
- asyncFunction.apply(this, arguments);
- return deferred.promise;
- };
- };
- /**
- * Returns a promise. If the object is already a Promise it is returned; otherwise
- * the object is wrapped in a Promise.
- * @param value The value to be treated as a Promise
- * @return A promise wrapping the original value
- */
- exports.as = function(value){
- if (value instanceof Promise) {
- return value;
- } else {
- var ret = defer();
- ret.resolve(value);
- return ret;
- }
- };
- });
- })(typeof define!="undefined"?define:function(factory){factory(require,exports)});
|