mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
7.4 KiB
211 lines
7.4 KiB
/*!
|
|
Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
|
|
Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
|
|
Licensed under The MIT License (http://opensource.org/licenses/MIT)
|
|
*/
|
|
|
|
/* promise states [Promises/A+ 2.1] */
|
|
var STATE_PENDING = 0; /* [Promises/A+ 2.1.1] */
|
|
var STATE_FULFILLED = 1; /* [Promises/A+ 2.1.2] */
|
|
var STATE_REJECTED = 2; /* [Promises/A+ 2.1.3] */
|
|
|
|
/* promise object constructor */
|
|
var api = function( executor ){
|
|
/* optionally support non-constructor/plain-function call */
|
|
if( !(this instanceof api) )
|
|
return new api( executor );
|
|
|
|
/* initialize object */
|
|
this.id = 'Thenable/1.0.7';
|
|
this.state = STATE_PENDING; /* initial state */
|
|
this.fulfillValue = undefined; /* initial value */ /* [Promises/A+ 1.3, 2.1.2.2] */
|
|
this.rejectReason = undefined; /* initial reason */ /* [Promises/A+ 1.5, 2.1.3.2] */
|
|
this.onFulfilled = []; /* initial handlers */
|
|
this.onRejected = []; /* initial handlers */
|
|
|
|
/* provide optional information-hiding proxy */
|
|
this.proxy = {
|
|
then: this.then.bind( this )
|
|
};
|
|
|
|
/* support optional executor function */
|
|
if( typeof executor === 'function' )
|
|
executor.call( this, this.fulfill.bind( this ), this.reject.bind( this ) );
|
|
};
|
|
|
|
/* promise API methods */
|
|
api.prototype = {
|
|
/* promise resolving methods */
|
|
fulfill: function( value ){ return deliver( this, STATE_FULFILLED, 'fulfillValue', value ); },
|
|
reject: function( value ){ return deliver( this, STATE_REJECTED, 'rejectReason', value ); },
|
|
|
|
/* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
|
|
then: function( onFulfilled, onRejected ){
|
|
var curr = this;
|
|
var next = new api(); /* [Promises/A+ 2.2.7] */
|
|
curr.onFulfilled.push(
|
|
resolver( onFulfilled, next, 'fulfill' ) ); /* [Promises/A+ 2.2.2/2.2.6] */
|
|
curr.onRejected.push(
|
|
resolver( onRejected, next, 'reject' ) ); /* [Promises/A+ 2.2.3/2.2.6] */
|
|
execute( curr );
|
|
return next.proxy; /* [Promises/A+ 2.2.7, 3.3] */
|
|
}
|
|
};
|
|
|
|
/* deliver an action */
|
|
var deliver = function( curr, state, name, value ){
|
|
if( curr.state === STATE_PENDING ){
|
|
curr.state = state; /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
|
|
curr[ name ] = value; /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
|
|
execute( curr );
|
|
}
|
|
return curr;
|
|
};
|
|
|
|
/* execute all handlers */
|
|
var execute = function( curr ){
|
|
if( curr.state === STATE_FULFILLED )
|
|
execute_handlers( curr, 'onFulfilled', curr.fulfillValue );
|
|
else if( curr.state === STATE_REJECTED )
|
|
execute_handlers( curr, 'onRejected', curr.rejectReason );
|
|
};
|
|
|
|
/* execute particular set of handlers */
|
|
var execute_handlers = function( curr, name, value ){
|
|
/* global setImmediate: true */
|
|
/* global setTimeout: true */
|
|
|
|
/* short-circuit processing */
|
|
if( curr[ name ].length === 0 )
|
|
return;
|
|
|
|
/* iterate over all handlers, exactly once */
|
|
var handlers = curr[ name ];
|
|
curr[ name ] = []; /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
|
|
var func = function(){
|
|
for( var i = 0; i < handlers.length; i++ )
|
|
handlers[ i ]( value ); /* [Promises/A+ 2.2.5] */
|
|
};
|
|
|
|
/* execute procedure asynchronously */ /* [Promises/A+ 2.2.4, 3.1] */
|
|
if( typeof setImmediate === 'function' )
|
|
setImmediate( func );
|
|
else
|
|
setTimeout( func, 0 );
|
|
};
|
|
|
|
/* generate a resolver function */
|
|
var resolver = function( cb, next, method ){
|
|
return function( value ){
|
|
if( typeof cb !== 'function' ) /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
|
|
next[ method ].call( next, value ); /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
|
|
else {
|
|
var result;
|
|
try { result = cb( value ); } /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
|
|
catch( e ){
|
|
next.reject( e ); /* [Promises/A+ 2.2.7.2] */
|
|
return;
|
|
}
|
|
resolve( next, result ); /* [Promises/A+ 2.2.7.1] */
|
|
}
|
|
};
|
|
};
|
|
|
|
/* "Promise Resolution Procedure" */ /* [Promises/A+ 2.3] */
|
|
var resolve = function( promise, x ){
|
|
/* sanity check arguments */ /* [Promises/A+ 2.3.1] */
|
|
if( promise === x || promise.proxy === x ){
|
|
promise.reject( new TypeError( 'cannot resolve promise with itself' ) );
|
|
return;
|
|
}
|
|
|
|
/* surgically check for a "then" method
|
|
(mainly to just call the "getter" of "then" only once) */
|
|
var then;
|
|
if( (typeof x === 'object' && x !== null) || typeof x === 'function' ){
|
|
try { then = x.then; } /* [Promises/A+ 2.3.3.1, 3.5] */
|
|
catch( e ){
|
|
promise.reject( e ); /* [Promises/A+ 2.3.3.2] */
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* handle own Thenables [Promises/A+ 2.3.2]
|
|
and similar "thenables" [Promises/A+ 2.3.3] */
|
|
if( typeof then === 'function' ){
|
|
var resolved = false;
|
|
try {
|
|
/* call retrieved "then" method */ /* [Promises/A+ 2.3.3.3] */
|
|
then.call( x,
|
|
/* resolvePromise */ /* [Promises/A+ 2.3.3.3.1] */
|
|
function( y ){
|
|
if( resolved ) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */
|
|
if( y === x ) /* [Promises/A+ 3.6] */
|
|
promise.reject( new TypeError( 'circular thenable chain' ) );
|
|
else
|
|
resolve( promise, y );
|
|
},
|
|
|
|
/* rejectPromise */ /* [Promises/A+ 2.3.3.3.2] */
|
|
function( r ){
|
|
if( resolved ) return; resolved = true; /* [Promises/A+ 2.3.3.3.3] */
|
|
promise.reject( r );
|
|
}
|
|
);
|
|
}
|
|
catch( e ){
|
|
if( !resolved ) /* [Promises/A+ 2.3.3.3.3] */
|
|
promise.reject( e ); /* [Promises/A+ 2.3.3.3.4] */
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* handle other values */
|
|
promise.fulfill( x ); /* [Promises/A+ 2.3.4, 2.3.3.4] */
|
|
};
|
|
|
|
// so we always have Promise.all()
|
|
api.all = function( ps ){
|
|
return new api(function( resolveAll, rejectAll ){
|
|
var vals = new Array( ps.length );
|
|
var doneCount = 0;
|
|
|
|
var fulfill = function( i, val ){
|
|
vals[ i ] = val;
|
|
doneCount++;
|
|
|
|
if( doneCount === ps.length ){
|
|
resolveAll( vals );
|
|
}
|
|
};
|
|
|
|
for( var i = 0; i < ps.length; i++ ){
|
|
(function( i ){
|
|
var p = ps[i];
|
|
var isPromise = p != null && p.then != null;
|
|
|
|
if( isPromise ){
|
|
p.then( function( val ){
|
|
fulfill( i, val );
|
|
}, function( err ){
|
|
rejectAll( err );
|
|
} );
|
|
} else {
|
|
var val = p;
|
|
fulfill( i, val );
|
|
}
|
|
})( i );
|
|
}
|
|
|
|
} );
|
|
};
|
|
|
|
api.resolve = function( val ){
|
|
return new api(function( resolve, reject ){ resolve( val ); });
|
|
};
|
|
|
|
api.reject = function( val ){
|
|
return new api(function( resolve, reject ){ reject( val ); });
|
|
};
|
|
|
|
export default typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
|
|
|