UNPKG

dojo

Version:

Dojo core is a powerful, lightweight library that makes common tasks quicker and easier. Animate elements, manipulate the DOM, and query with easy CSS syntax, all without sacrificing performance.

321 lines (284 loc) 9.8 kB
define([ "./has", "./_base/lang", "./errors/CancelError", "./promise/Promise", "./has!config-deferredInstrumentation?./promise/instrumentation" ], function(has, lang, CancelError, Promise, instrumentation){ "use strict"; // module: // dojo/Deferred var PROGRESS = 0, RESOLVED = 1, REJECTED = 2; var FULFILLED_ERROR_MESSAGE = "This deferred has already been fulfilled."; var freezeObject = Object.freeze || function(){}; var signalWaiting = function(waiting, type, result, rejection, deferred){ if(has("config-deferredInstrumentation")){ if(type === REJECTED && Deferred.instrumentRejected && waiting.length === 0){ Deferred.instrumentRejected(result, false, rejection, deferred); } } for(var i = 0; i < waiting.length; i++){ signalListener(waiting[i], type, result, rejection); } }; var signalListener = function(listener, type, result, rejection){ var func = listener[type]; var deferred = listener.deferred; if(func){ try{ var newResult = func(result); if(type === PROGRESS){ if(typeof newResult !== "undefined"){ signalDeferred(deferred, type, newResult); } }else{ if(newResult && typeof newResult.then === "function"){ listener.cancel = newResult.cancel; newResult.then( // Only make resolvers if they're actually going to be used makeDeferredSignaler(deferred, RESOLVED), makeDeferredSignaler(deferred, REJECTED), makeDeferredSignaler(deferred, PROGRESS)); return; } signalDeferred(deferred, RESOLVED, newResult); } }catch(error){ signalDeferred(deferred, REJECTED, error); } }else{ signalDeferred(deferred, type, result); } if(has("config-deferredInstrumentation")){ if(type === REJECTED && Deferred.instrumentRejected){ Deferred.instrumentRejected(result, !!func, rejection, deferred.promise); } } }; var makeDeferredSignaler = function(deferred, type){ return function(value){ signalDeferred(deferred, type, value); }; }; var signalDeferred = function(deferred, type, result){ if(!deferred.isCanceled()){ switch(type){ case PROGRESS: deferred.progress(result); break; case RESOLVED: deferred.resolve(result); break; case REJECTED: deferred.reject(result); break; } } }; var Deferred = function(canceler){ // summary: // Creates a new deferred. This API is preferred over // `dojo/_base/Deferred`. // description: // Creates a new deferred, as an abstraction over (primarily) // asynchronous operations. The deferred is the private interface // that should not be returned to calling code. That's what the // `promise` is for. See `dojo/promise/Promise`. // canceler: Function? // Will be invoked if the deferred is canceled. The canceler // receives the reason the deferred was canceled as its argument. // The deferred is rejected with its return value, or a new // `dojo/errors/CancelError` instance. // promise: dojo/promise/Promise // The public promise object that clients can add callbacks to. var promise = this.promise = new Promise(); var deferred = this; var fulfilled, result, rejection; var canceled = false; var waiting = []; if(has("config-deferredInstrumentation") && Error.captureStackTrace){ Error.captureStackTrace(deferred, Deferred); Error.captureStackTrace(promise, Deferred); } this.isResolved = promise.isResolved = function(){ // summary: // Checks whether the deferred has been resolved. // returns: Boolean return fulfilled === RESOLVED; }; this.isRejected = promise.isRejected = function(){ // summary: // Checks whether the deferred has been rejected. // returns: Boolean return fulfilled === REJECTED; }; this.isFulfilled = promise.isFulfilled = function(){ // summary: // Checks whether the deferred has been resolved or rejected. // returns: Boolean return !!fulfilled; }; this.isCanceled = promise.isCanceled = function(){ // summary: // Checks whether the deferred has been canceled. // returns: Boolean return canceled; }; this.progress = function(update, strict){ // summary: // Emit a progress update on the deferred. // description: // Emit a progress update on the deferred. Progress updates // can be used to communicate updates about the asynchronous // operation before it has finished. // update: any // The progress update. Passed to progbacks. // strict: Boolean? // If strict, will throw an error if the deferred has already // been fulfilled and consequently no progress can be emitted. // returns: dojo/promise/Promise // Returns the original promise for the deferred. if(!fulfilled){ signalWaiting(waiting, PROGRESS, update, null, deferred); return promise; }else if(strict === true){ throw new Error(FULFILLED_ERROR_MESSAGE); }else{ return promise; } }; this.resolve = function(value, strict){ // summary: // Resolve the deferred. // description: // Resolve the deferred, putting it in a success state. // value: any // The result of the deferred. Passed to callbacks. // strict: Boolean? // If strict, will throw an error if the deferred has already // been fulfilled and consequently cannot be resolved. // returns: dojo/promise/Promise // Returns the original promise for the deferred. if(!fulfilled){ // Set fulfilled, store value. After signaling waiting listeners unset // waiting. signalWaiting(waiting, fulfilled = RESOLVED, result = value, null, deferred); waiting = null; return promise; }else if(strict === true){ throw new Error(FULFILLED_ERROR_MESSAGE); }else{ return promise; } }; var reject = this.reject = function(error, strict){ // summary: // Reject the deferred. // description: // Reject the deferred, putting it in an error state. // error: any // The error result of the deferred. Passed to errbacks. // strict: Boolean? // If strict, will throw an error if the deferred has already // been fulfilled and consequently cannot be rejected. // returns: dojo/promise/Promise // Returns the original promise for the deferred. if(!fulfilled){ if(has("config-deferredInstrumentation") && Error.captureStackTrace){ Error.captureStackTrace(rejection = {}, reject); } signalWaiting(waiting, fulfilled = REJECTED, result = error, rejection, deferred); waiting = null; return promise; }else if(strict === true){ throw new Error(FULFILLED_ERROR_MESSAGE); }else{ return promise; } }; this.then = promise.then = function(callback, errback, progback){ // summary: // Add new callbacks to the deferred. // description: // Add new callbacks to the deferred. Callbacks can be added // before or after the deferred is fulfilled. // callback: Function? // Callback to be invoked when the promise is resolved. // Receives the resolution value. // errback: Function? // Callback to be invoked when the promise is rejected. // Receives the rejection error. // progback: Function? // Callback to be invoked when the promise emits a progress // update. Receives the progress update. // returns: dojo/promise/Promise // Returns a new promise for the result of the callback(s). // This can be used for chaining many asynchronous operations. var listener = [progback, callback, errback]; // Ensure we cancel the promise we're waiting for, or if callback/errback // have returned a promise, cancel that one. listener.cancel = promise.cancel; listener.deferred = new Deferred(function(reason){ // Check whether cancel is really available, returned promises are not // required to expose `cancel` return listener.cancel && listener.cancel(reason); }); if(fulfilled && !waiting){ signalListener(listener, fulfilled, result, rejection); }else{ waiting.push(listener); } return listener.deferred.promise; }; this.cancel = promise.cancel = function(reason, strict){ // summary: // Inform the deferred it may cancel its asynchronous operation. // description: // Inform the deferred it may cancel its asynchronous operation. // The deferred's (optional) canceler is invoked and the // deferred will be left in a rejected state. Can affect other // promises that originate with the same deferred. // reason: any // A message that may be sent to the deferred's canceler, // explaining why it's being canceled. // strict: Boolean? // If strict, will throw an error if the deferred has already // been fulfilled and consequently cannot be canceled. // returns: any // Returns the rejection reason if the deferred was canceled // normally. if(!fulfilled){ // Cancel can be called even after the deferred is fulfilled if(canceler){ var returnedReason = canceler(reason); reason = typeof returnedReason === "undefined" ? reason : returnedReason; } canceled = true; if(!fulfilled){ // Allow canceler to provide its own reason, but fall back to a CancelError if(typeof reason === "undefined"){ reason = new CancelError(); } reject(reason); return reason; }else if(fulfilled === REJECTED && result === reason){ return reason; } }else if(strict === true){ throw new Error(FULFILLED_ERROR_MESSAGE); } }; freezeObject(promise); }; Deferred.prototype.toString = function(){ // returns: String // Returns `[object Deferred]`. return "[object Deferred]"; }; if(instrumentation){ instrumentation(Deferred); } return Deferred; });