-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
857 additions
and
261 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,289 +1,58 @@ | ||
'use strict'; | ||
var LIBRARY = require('./_library') | ||
, global = require('./_global') | ||
, ctx = require('./_ctx') | ||
var global = require('./_global') | ||
, classof = require('./_classof') | ||
, $export = require('./_export') | ||
, isObject = require('./_is-object') | ||
, anObject = require('./_an-object') | ||
, aFunction = require('./_a-function') | ||
, anInstance = require('./_an-instance') | ||
, forOf = require('./_for-of') | ||
, from = require('./_array-from-iterable') | ||
, assign = require('./_object-assign') | ||
, redefine = require('./_redefine') | ||
, wks = require('./_wks') | ||
, setProto = require('./_set-proto').set | ||
, speciesConstructor = require('./_species-constructor') | ||
, task = require('./_task').set | ||
, microtask = require('./_microtask') | ||
, PROMISE = 'Promise' | ||
, TypeError = global.TypeError | ||
, process = global.process | ||
, $Promise = global[PROMISE] | ||
, isNode = classof(process) == 'process' | ||
, empty = function(){ /* empty */ } | ||
, Internal, GenericPromiseCapability, Wrapper; | ||
, Yaku = require('./yaku'); | ||
|
||
var USE_NATIVE = !!function(){ | ||
try { | ||
// correct subclassing with @@species support | ||
var promise = $Promise.resolve(1) | ||
, FakePromise1 = promise.constructor = function(exec){ exec(empty, empty); } | ||
, FakePromise2 = function(exec){ exec(empty, empty); }; | ||
require('./_object-dp')(FakePromise1, require('./_wks')('species'), {value: FakePromise2}); | ||
require('./_object-dp')(FakePromise1, wks('species'), {value: FakePromise2}); | ||
// unhandled rejections tracking support, NodeJS Promise without it fails @@species test | ||
return (isNode || typeof PromiseRejectionEvent == 'function') && promise.then(empty) instanceof FakePromise2; | ||
} catch(e){ /* empty */ } | ||
}(); | ||
|
||
// helpers | ||
var sameConstructor = function(a, b){ | ||
// with library wrapper special case | ||
return a === b || a === $Promise && b === Wrapper; | ||
}; | ||
var isThenable = function(it){ | ||
var then; | ||
return isObject(it) && typeof (then = it.then) == 'function' ? then : false; | ||
}; | ||
var newPromiseCapability = function(C){ | ||
return sameConstructor($Promise, C) | ||
? new PromiseCapability(C) | ||
: new GenericPromiseCapability(C); | ||
}; | ||
var PromiseCapability = GenericPromiseCapability = function(C){ | ||
var resolve, reject; | ||
this.promise = new C(function($$resolve, $$reject){ | ||
if(resolve !== undefined || reject !== undefined)throw TypeError('Bad Promise constructor'); | ||
resolve = $$resolve; | ||
reject = $$reject; | ||
}); | ||
this.resolve = aFunction(resolve); | ||
this.reject = aFunction(reject); | ||
}; | ||
var perform = function(exec){ | ||
try { | ||
exec(); | ||
} catch(e){ | ||
return {error: e}; | ||
} | ||
}; | ||
var notify = function(promise, isReject){ | ||
if(promise._n)return; | ||
promise._n = true; | ||
var chain = promise._c; | ||
microtask(function(){ | ||
var value = promise._v | ||
, ok = promise._s == 1 | ||
, i = 0; | ||
var run = function(reaction){ | ||
var handler = ok ? reaction.ok : reaction.fail | ||
, resolve = reaction.resolve | ||
, reject = reaction.reject | ||
, result, then; | ||
try { | ||
if(handler){ | ||
if(!ok){ | ||
if(promise._h == 2)onHandleUnhandled(promise); | ||
promise._h = 1; | ||
} | ||
result = handler === true ? value : handler(value); | ||
if(result === reaction.promise){ | ||
reject(TypeError('Promise-chain cycle')); | ||
} else if(then = isThenable(result)){ | ||
then.call(result, resolve, reject); | ||
} else resolve(result); | ||
} else reject(value); | ||
} catch(e){ | ||
reject(e); | ||
} | ||
}; | ||
while(chain.length > i)run(chain[i++]); // variable length - can't use forEach | ||
promise._c = []; | ||
promise._n = false; | ||
if(isReject && !promise._h)onUnhandled(promise); | ||
}); | ||
}; | ||
var onUnhandled = function(promise){ | ||
task.call(global, function(){ | ||
if(isUnhandled(promise)){ | ||
var value = promise._v | ||
, handler, console; | ||
if(isNode){ | ||
process.emit('unhandledRejection', value, promise); | ||
} else if(handler = global.onunhandledrejection){ | ||
handler({promise: promise, reason: value}); | ||
} else if((console = global.console) && console.error){ | ||
console.error('Unhandled promise rejection', value); | ||
} promise._h = 2; | ||
} promise._a = undefined; | ||
}); | ||
}; | ||
var isUnhandled = function(promise){ | ||
var chain = promise._a || promise._c | ||
, i = 0 | ||
, reaction; | ||
if(promise._h == 1)return false; | ||
while(chain.length > i){ | ||
reaction = chain[i++]; | ||
if(reaction.fail || !isUnhandled(reaction.promise))return false; | ||
} return true; | ||
}; | ||
var onHandleUnhandled = function(promise){ | ||
task.call(global, function(){ | ||
var handler; | ||
if(isNode){ | ||
process.emit('rejectionHandled', promise); | ||
} else if(handler = global.onrejectionhandled){ | ||
handler({promise: promise, reason: promise._v}); | ||
} | ||
}); | ||
}; | ||
var $reject = function(value){ | ||
var promise = this; | ||
if(promise._d)return; | ||
promise._d = true; | ||
promise = promise._w || promise; // unwrap | ||
promise._v = value; | ||
promise._s = 2; | ||
if(!promise._a)promise._a = promise._c.slice(); | ||
notify(promise, true); | ||
}; | ||
var $resolve = function(value){ | ||
var promise = this | ||
, then; | ||
if(promise._d)return; | ||
promise._d = true; | ||
promise = promise._w || promise; // unwrap | ||
try { | ||
if(promise === value)throw TypeError("Promise can't be resolved itself"); | ||
if(then = isThenable(value)){ | ||
microtask(function(){ | ||
var wrapper = {_w: promise, _d: false}; // wrap | ||
try { | ||
then.call(value, ctx($resolve, wrapper, 1), ctx($reject, wrapper, 1)); | ||
} catch(e){ | ||
$reject.call(wrapper, e); | ||
} | ||
}); | ||
} else { | ||
promise._v = value; | ||
promise._s = 1; | ||
notify(promise, false); | ||
} | ||
} catch(e){ | ||
$reject.call({_w: promise, _d: false}, e); // wrap | ||
} | ||
}; | ||
|
||
// constructor polyfill | ||
if(!USE_NATIVE){ | ||
// 25.4.3.1 Promise(executor) | ||
$Promise = function Promise(executor){ | ||
anInstance(this, $Promise, PROMISE, '_h'); | ||
aFunction(executor); | ||
Internal.call(this); | ||
try { | ||
executor(ctx($resolve, this, 1), ctx($reject, this, 1)); | ||
} catch(err){ | ||
$reject.call(this, err); | ||
} | ||
}; | ||
Internal = function Promise(executor){ | ||
this._c = []; // <- awaiting reactions | ||
this._a = undefined; // <- checked in isUnhandled reactions | ||
this._s = 0; // <- state | ||
this._d = false; // <- done | ||
this._v = undefined; // <- value | ||
this._h = 0; // <- rejection state, 0 - default, 1 - handled, 2 - unhandled | ||
this._n = false; // <- notify | ||
|
||
// Promise need two well-known symbols. | ||
Yaku.Symbol = { | ||
iterator: wks('iterator'), | ||
species: wks('species'), | ||
}; | ||
Internal.prototype = require('./_redefine-all')($Promise.prototype, { | ||
// 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected) | ||
then: function then(onFulfilled, onRejected){ | ||
var reaction = newPromiseCapability(speciesConstructor(this, $Promise)); | ||
reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true; | ||
reaction.fail = typeof onRejected == 'function' && onRejected; | ||
this._c.push(reaction); | ||
if(this._a)this._a.push(reaction); | ||
if(this._s)notify(this, false); | ||
return reaction.promise; | ||
}, | ||
// 25.4.5.1 Promise.prototype.catch(onRejected) | ||
'catch': function(onRejected){ | ||
return this.then(undefined, onRejected); | ||
|
||
// Use the special optimized microtask. | ||
Yaku.nextTick = require('./_microtask'); | ||
|
||
// For subclass construction. | ||
Yaku.speciesConstructor = require('./_species-constructor'); | ||
|
||
$Promise = Yaku; | ||
|
||
var redefineAll = function (target) { | ||
var k; | ||
for (k in target) { | ||
var v = target[k]; | ||
delete target[k]; | ||
redefine(target, k, v); | ||
} | ||
}); | ||
PromiseCapability = function(){ | ||
var promise = new Internal; | ||
this.promise = promise; | ||
this.resolve = ctx($resolve, promise, 1); | ||
this.reject = ctx($reject, promise, 1); | ||
}; | ||
} | ||
|
||
redefineAll($Promise); | ||
redefineAll($Promise.prototype); | ||
} | ||
|
||
$export($export.G + $export.W + $export.F * !USE_NATIVE, {Promise: $Promise}); | ||
require('./_set-to-string-tag')($Promise, PROMISE); | ||
require('./_set-species')(PROMISE); | ||
Wrapper = require('./_core')[PROMISE]; | ||
|
||
// statics | ||
$export($export.S + $export.F * !USE_NATIVE, PROMISE, { | ||
// 25.4.4.5 Promise.reject(r) | ||
reject: function reject(r){ | ||
var capability = newPromiseCapability(this) | ||
, $$reject = capability.reject; | ||
$$reject(r); | ||
return capability.promise; | ||
} | ||
}); | ||
$export($export.S + $export.F * (LIBRARY || !USE_NATIVE), PROMISE, { | ||
// 25.4.4.6 Promise.resolve(x) | ||
resolve: function resolve(x){ | ||
// instanceof instead of internal slot check because we should fix it without replacement native Promise core | ||
if(x instanceof $Promise && sameConstructor(x.constructor, this))return x; | ||
var capability = newPromiseCapability(this) | ||
, $$resolve = capability.resolve; | ||
$$resolve(x); | ||
return capability.promise; | ||
} | ||
}); | ||
$export($export.S + $export.F * !(USE_NATIVE && require('./_iter-detect')(function(iter){ | ||
$Promise.all(iter)['catch'](empty); | ||
})), PROMISE, { | ||
// 25.4.4.1 Promise.all(iterable) | ||
all: function all(iterable){ | ||
var C = this | ||
, capability = newPromiseCapability(C) | ||
, resolve = capability.resolve | ||
, reject = capability.reject; | ||
var abrupt = perform(function(){ | ||
var values = from(iterable) | ||
, remaining = values.length | ||
, results = Array(remaining); | ||
var f = function(promise, index){ | ||
var alreadyCalled = false; | ||
C.resolve(promise).then(function(value){ | ||
if(alreadyCalled)return; | ||
alreadyCalled = true; | ||
results[index] = value; | ||
--remaining || resolve(results); | ||
}, reject); | ||
}; | ||
if(remaining)for(var i = 0, l = values.length; l > i; i++)f(values[i], i); | ||
else resolve(results); | ||
}); | ||
if(abrupt)reject(abrupt.error); | ||
return capability.promise; | ||
}, | ||
// 25.4.4.4 Promise.race(iterable) | ||
race: function race(iterable){ | ||
var C = this | ||
, capability = newPromiseCapability(C) | ||
, reject = capability.reject; | ||
var abrupt = perform(function(){ | ||
forOf(iterable, false, function(promise){ | ||
C.resolve(promise).then(capability.resolve, reject); | ||
}); | ||
}); | ||
if(abrupt)reject(abrupt.error); | ||
return capability.promise; | ||
} | ||
}); |
Oops, something went wrong.