320 lines
9.4 KiB
JavaScript
320 lines
9.4 KiB
JavaScript
define("dojo/Deferred", [
|
|
"./has",
|
|
"./_base/lang",
|
|
"./errors/CancelError",
|
|
"./promise/Promise",
|
|
"./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( 1 ){
|
|
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( 1 ){
|
|
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( 1 && 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( 1 && 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;
|
|
});
|