cytoscape
Version:
Graph theory (a.k.a. network) library for analysis and visualisation
212 lines (183 loc) • 7.59 kB
JavaScript
/*!
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