123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- 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;
- });
|