UNPKG

core-js

Version:
257 lines (251 loc) 7.81 kB
'use strict'; var $ = require('./$') , LIBRARY = require('./$.library') , global = require('./$.global') , ctx = require('./$.ctx') , classof = require('./$.classof') , $def = require('./$.def') , isObject = require('./$.is-object') , anObject = require('./$.an-object') , aFunction = require('./$.a-function') , strictNew = require('./$.strict-new') , forOf = require('./$.for-of') , setProto = require('./$.set-proto').set , same = require('./$.same') , species = require('./$.species') , SPECIES = require('./$.wks')('species') , RECORD = require('./$.uid')('record') , asap = require('./$.microtask') , PROMISE = 'Promise' , process = global.process , isNode = classof(process) == 'process' , P = global[PROMISE] , Wrapper; var testResolve = function(sub){ var test = new P(function(){}); if(sub)test.constructor = Object; return P.resolve(test) === test; }; var useNative = function(){ var works = false; function P2(x){ var self = new P(x); setProto(self, P2.prototype); return self; } try { works = P && P.resolve && testResolve(); setProto(P2, P); P2.prototype = $.create(P.prototype, {constructor: {value: P2}}); // actual Firefox has broken subclass support, test that if(!(P2.resolve(5).then(function(){}) instanceof P2)){ works = false; } // actual V8 bug, https://code.google.com/p/v8/issues/detail?id=4162 if(works && require('./$.support-desc')){ var thenableThenGotten = false; P.resolve($.setDesc({}, 'then', { get: function(){ thenableThenGotten = true; } })); works = thenableThenGotten; } } catch(e){ works = false; } return works; }(); // helpers var isPromise = function(it){ return isObject(it) && (useNative ? classof(it) == 'Promise' : RECORD in it); }; var sameConstructor = function(a, b){ // library wrapper special case if(LIBRARY && a === P && b === Wrapper)return true; return same(a, b); }; var getConstructor = function(C){ var S = anObject(C)[SPECIES]; return S != undefined ? S : C; }; var isThenable = function(it){ var then; return isObject(it) && typeof (then = it.then) == 'function' ? then : false; }; var notify = function(record, isReject){ if(record.n)return; record.n = true; var chain = record.c; asap(function(){ var value = record.v , ok = record.s == 1 , i = 0; var run = function(react){ var cb = ok ? react.ok : react.fail , ret, then; try { if(cb){ if(!ok)record.h = true; ret = cb === true ? value : cb(value); if(ret === react.P){ react.rej(TypeError('Promise-chain cycle')); } else if(then = isThenable(ret)){ then.call(ret, react.res, react.rej); } else react.res(ret); } else react.rej(value); } catch(err){ react.rej(err); } }; while(chain.length > i)run(chain[i++]); // variable length - can't use forEach chain.length = 0; record.n = false; if(isReject)setTimeout(function(){ if(isUnhandled(record.p)){ if(isNode){ process.emit('unhandledRejection', value, record.p); } else if(global.console && console.error){ console.error('Unhandled promise rejection', value); } } record.a = undefined; }, 1); }); }; var isUnhandled = function(promise){ var record = promise[RECORD] , chain = record.a || record.c , i = 0 , react; if(record.h)return false; while(chain.length > i){ react = chain[i++]; if(react.fail || !isUnhandled(react.P))return false; } return true; }; var $reject = function(value){ var record = this; if(record.d)return; record.d = true; record = record.r || record; // unwrap record.v = value; record.s = 2; record.a = record.c.slice(); notify(record, true); }; var $resolve = function(value){ var record = this , then; if(record.d)return; record.d = true; record = record.r || record; // unwrap try { if(then = isThenable(value)){ asap(function(){ var wrapper = {r: record, d: false}; // wrap try { then.call(value, ctx($resolve, wrapper, 1), ctx($reject, wrapper, 1)); } catch(e){ $reject.call(wrapper, e); } }); } else { record.v = value; record.s = 1; notify(record, false); } } catch(e){ $reject.call({r: record, d: false}, e); // wrap } }; // constructor polyfill if(!useNative){ // 25.4.3.1 Promise(executor) P = function Promise(executor){ aFunction(executor); var record = { p: strictNew(this, P, PROMISE), // <- promise c: [], // <- awaiting reactions a: undefined, // <- checked in isUnhandled reactions s: 0, // <- state d: false, // <- done v: undefined, // <- value h: false, // <- handled rejection n: false // <- notify }; this[RECORD] = record; try { executor(ctx($resolve, record, 1), ctx($reject, record, 1)); } catch(err){ $reject.call(record, err); } }; require('./$.mix')(P.prototype, { // 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected) then: function then(onFulfilled, onRejected){ var S = anObject(anObject(this).constructor)[SPECIES]; var react = { ok: typeof onFulfilled == 'function' ? onFulfilled : true, fail: typeof onRejected == 'function' ? onRejected : false }; var promise = react.P = new (S != undefined ? S : P)(function(res, rej){ react.res = aFunction(res); react.rej = aFunction(rej); }); var record = this[RECORD]; record.c.push(react); if(record.a)record.a.push(react); if(record.s)notify(record, false); return promise; }, // 25.4.5.1 Promise.prototype.catch(onRejected) 'catch': function(onRejected){ return this.then(undefined, onRejected); } }); } // export $def($def.G + $def.W + $def.F * !useNative, {Promise: P}); require('./$.tag')(P, PROMISE); species(P); species(Wrapper = require('./$.core')[PROMISE]); // statics $def($def.S + $def.F * !useNative, PROMISE, { // 25.4.4.5 Promise.reject(r) reject: function reject(r){ return new this(function(res, rej){ rej(r); }); } }); $def($def.S + $def.F * (!useNative || testResolve(true)), PROMISE, { // 25.4.4.6 Promise.resolve(x) resolve: function resolve(x){ return isPromise(x) && sameConstructor(x.constructor, this) ? x : new this(function(res){ res(x); }); } }); $def($def.S + $def.F * !(useNative && require('./$.iter-detect')(function(iter){ P.all(iter)['catch'](function(){}); })), PROMISE, { // 25.4.4.1 Promise.all(iterable) all: function all(iterable){ var C = getConstructor(this) , values = []; return new C(function(res, rej){ forOf(iterable, false, values.push, values); var remaining = values.length , results = Array(remaining); if(remaining)$.each.call(values, function(promise, index){ C.resolve(promise).then(function(value){ results[index] = value; --remaining || res(results); }, rej); }); else res(results); }); }, // 25.4.4.4 Promise.race(iterable) race: function race(iterable){ var C = getConstructor(this); return new C(function(res, rej){ forOf(iterable, false, function(promise){ C.resolve(promise).then(res, rej); }); }); } });