mirror of
https://github.com/kiwix/kiwix-js-pwa.git
synced 2025-08-11 07:19:43 -04:00

Former-commit-id: 2ce10066e6d7b53aa1390d7e75189f18c7b59b8a [formerly e7fe66c76d904f511bfa246c059dade2d12911b6 [formerly 40d3b58c2cf6821c94cb138f86d45c498698beed]] Former-commit-id: f3cc4ecfb825af85e764ab89d1b350cdaca4b5e2 Former-commit-id: fa761722a44f952ce2dd17b8c9944b2b4f83adbf
879 lines
20 KiB
JavaScript
879 lines
20 KiB
JavaScript
/*
|
|
Yaku v0.19.3
|
|
(c) 2015 Yad Smood. http://ysmood.org
|
|
License MIT
|
|
*/
|
|
/*
|
|
Yaku v0.17.9
|
|
(c) 2015 Yad Smood. http://ysmood.org
|
|
License MIT
|
|
*/
|
|
|
|
(function () {
|
|
'use strict';
|
|
|
|
var $undefined
|
|
, $null = null
|
|
, isBrowser = typeof self === 'object'
|
|
, root = self
|
|
, nativePromise = root.Promise
|
|
, process = root.process
|
|
, console = root.console
|
|
, isLongStackTrace = true
|
|
, Arr = Array
|
|
, Err = Error
|
|
|
|
, $rejected = 1
|
|
, $resolved = 2
|
|
, $pending = 3
|
|
|
|
, $Symbol = 'Symbol'
|
|
, $iterator = 'iterator'
|
|
, $species = 'species'
|
|
, $speciesKey = $Symbol + '(' + $species + ')'
|
|
, $return = 'return'
|
|
|
|
, $unhandled = '_uh'
|
|
, $promiseTrace = '_pt'
|
|
, $settlerTrace = '_st'
|
|
|
|
, $invalidThis = 'Invalid this'
|
|
, $invalidArgument = 'Invalid argument'
|
|
, $fromPrevious = '\nFrom previous '
|
|
, $promiseCircularChain = 'Chaining cycle detected for promise'
|
|
, $unhandledRejectionMsg = 'Uncaught (in promise)'
|
|
, $rejectionHandled = 'rejectionHandled'
|
|
, $unhandledRejection = 'unhandledRejection'
|
|
|
|
, $tryCatchFn
|
|
, $tryCatchThis
|
|
, $tryErr = { e: $null }
|
|
, $noop = function () {}
|
|
, $cleanStackReg = /^.+\/node_modules\/yaku\/.+\n?/mg
|
|
;
|
|
|
|
/**
|
|
* This class follows the [Promises/A+](https://promisesaplus.com) and
|
|
* [ES6](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-promise-objects) spec
|
|
* with some extra helpers.
|
|
* @param {Function} executor Function object with two arguments resolve, reject.
|
|
* The first argument fulfills the promise, the second argument rejects it.
|
|
* We can call these functions, once our operation is completed.
|
|
*/
|
|
var Yaku = function (executor) {
|
|
var self = this,
|
|
err;
|
|
|
|
// "this._s" is the internao state of: pending, resolved or rejected
|
|
// "this._v" is the internal value
|
|
|
|
if (!isObject(self) || self._s !== $undefined)
|
|
throw genTypeError($invalidThis);
|
|
|
|
self._s = $pending;
|
|
|
|
if (isLongStackTrace) self[$promiseTrace] = genTraceInfo();
|
|
|
|
if (executor !== $noop) {
|
|
if (!isFunction(executor))
|
|
throw genTypeError($invalidArgument);
|
|
|
|
err = genTryCatcher(executor)(
|
|
genSettler(self, $resolved),
|
|
genSettler(self, $rejected)
|
|
);
|
|
|
|
if (err === $tryErr)
|
|
settlePromise(self, $rejected, err.e);
|
|
}
|
|
};
|
|
|
|
Yaku['default'] = Yaku;
|
|
|
|
extend(Yaku.prototype, {
|
|
/**
|
|
* Appends fulfillment and rejection handlers to the promise,
|
|
* and returns a new promise resolving to the return value of the called handler.
|
|
* @param {Function} onFulfilled Optional. Called when the Promise is resolved.
|
|
* @param {Function} onRejected Optional. Called when the Promise is rejected.
|
|
* @return {Yaku} It will return a new Yaku which will resolve or reject after
|
|
* @example
|
|
* the current Promise.
|
|
* ```js
|
|
* var Promise = require('yaku');
|
|
* var p = Promise.resolve(10);
|
|
*
|
|
* p.then((v) => {
|
|
* console.log(v);
|
|
* });
|
|
* ```
|
|
*/
|
|
then: function (onFulfilled, onRejected) {
|
|
if (this._s === undefined) throw genTypeError();
|
|
|
|
return addHandler(
|
|
this,
|
|
newCapablePromise(Yaku.speciesConstructor(this, Yaku)),
|
|
onFulfilled,
|
|
onRejected
|
|
);
|
|
},
|
|
|
|
/**
|
|
* The `catch()` method returns a Promise and deals with rejected cases only.
|
|
* It behaves the same as calling `Promise.prototype.then(undefined, onRejected)`.
|
|
* @param {Function} onRejected A Function called when the Promise is rejected.
|
|
* This function has one argument, the rejection reason.
|
|
* @return {Yaku} A Promise that deals with rejected cases only.
|
|
* @example
|
|
* ```js
|
|
* var Promise = require('yaku');
|
|
* var p = Promise.reject(new Error("ERR"));
|
|
*
|
|
* p['catch']((v) => {
|
|
* console.log(v);
|
|
* });
|
|
* ```
|
|
*/
|
|
'catch': function (onRejected) {
|
|
return this.then($undefined, onRejected);
|
|
},
|
|
|
|
/**
|
|
* Register a callback to be invoked when a promise is settled (either fulfilled or rejected).
|
|
* Similar with the try-catch-finally, it's often used for cleanup.
|
|
* @param {Function} onFinally A Function called when the Promise is settled.
|
|
* It will not receive any argument.
|
|
* @return {Yaku} A Promise that will reject if onFinally throws an error or returns a rejected promise.
|
|
* Else it will resolve previous promise's final state (either fulfilled or rejected).
|
|
* @example
|
|
* ```js
|
|
* var Promise = require('yaku');
|
|
* var p = Math.random() > 0.5 ? Promise.resolve() : Promise.reject();
|
|
* p.finally(() => {
|
|
* console.log('finally');
|
|
* });
|
|
* ```
|
|
*/
|
|
'finally': function (onFinally) {
|
|
return this.then(function (val) {
|
|
return Yaku.resolve(onFinally()).then(function () {
|
|
return val;
|
|
});
|
|
}, function (err) {
|
|
return Yaku.resolve(onFinally()).then(function () {
|
|
throw err;
|
|
});
|
|
});
|
|
},
|
|
|
|
// The number of current promises that attach to this Yaku instance.
|
|
_c: 0,
|
|
|
|
// The parent Yaku.
|
|
_p: $null
|
|
});
|
|
|
|
/**
|
|
* The `Promise.resolve(value)` method returns a Promise object that is resolved with the given value.
|
|
* If the value is a thenable (i.e. has a then method), the returned promise will "follow" that thenable,
|
|
* adopting its eventual state; otherwise the returned promise will be fulfilled with the value.
|
|
* @param {Any} value Argument to be resolved by this Promise.
|
|
* Can also be a Promise or a thenable to resolve.
|
|
* @return {Yaku}
|
|
* @example
|
|
* ```js
|
|
* var Promise = require('yaku');
|
|
* var p = Promise.resolve(10);
|
|
* ```
|
|
*/
|
|
Yaku.resolve = function (val) {
|
|
return isYaku(val) ? val : settleWithX(newCapablePromise(this), val);
|
|
};
|
|
|
|
/**
|
|
* The `Promise.reject(reason)` method returns a Promise object that is rejected with the given reason.
|
|
* @param {Any} reason Reason why this Promise rejected.
|
|
* @return {Yaku}
|
|
* @example
|
|
* ```js
|
|
* var Promise = require('yaku');
|
|
* var p = Promise.reject(new Error("ERR"));
|
|
* ```
|
|
*/
|
|
Yaku.reject = function (reason) {
|
|
return settlePromise(newCapablePromise(this), $rejected, reason);
|
|
};
|
|
|
|
/**
|
|
* The `Promise.race(iterable)` method returns a promise that resolves or rejects
|
|
* as soon as one of the promises in the iterable resolves or rejects,
|
|
* with the value or reason from that promise.
|
|
* @param {iterable} iterable An iterable object, such as an Array.
|
|
* @return {Yaku} The race function returns a Promise that is settled
|
|
* the same way as the first passed promise to settle.
|
|
* It resolves or rejects, whichever happens first.
|
|
* @example
|
|
* ```js
|
|
* var Promise = require('yaku');
|
|
* Promise.race([
|
|
* 123,
|
|
* Promise.resolve(0)
|
|
* ])
|
|
* .then((value) => {
|
|
* console.log(value); // => 123
|
|
* });
|
|
* ```
|
|
*/
|
|
Yaku.race = function (iterable) {
|
|
var self = this
|
|
, p = newCapablePromise(self)
|
|
|
|
, resolve = function (val) {
|
|
settlePromise(p, $resolved, val);
|
|
}
|
|
|
|
, reject = function (val) {
|
|
settlePromise(p, $rejected, val);
|
|
}
|
|
|
|
, ret = genTryCatcher(each)(iterable, function (v) {
|
|
self.resolve(v).then(resolve, reject);
|
|
});
|
|
|
|
if (ret === $tryErr) return self.reject(ret.e);
|
|
|
|
return p;
|
|
};
|
|
|
|
/**
|
|
* The `Promise.all(iterable)` method returns a promise that resolves when
|
|
* all of the promises in the iterable argument have resolved.
|
|
*
|
|
* The result is passed as an array of values from all the promises.
|
|
* If something passed in the iterable array is not a promise,
|
|
* it's converted to one by Promise.resolve. If any of the passed in promises rejects,
|
|
* the all Promise immediately rejects with the value of the promise that rejected,
|
|
* discarding all the other promises whether or not they have resolved.
|
|
* @param {iterable} iterable An iterable object, such as an Array.
|
|
* @return {Yaku}
|
|
* @example
|
|
* ```js
|
|
* var Promise = require('yaku');
|
|
* Promise.all([
|
|
* 123,
|
|
* Promise.resolve(0)
|
|
* ])
|
|
* .then((values) => {
|
|
* console.log(values); // => [123, 0]
|
|
* });
|
|
* ```
|
|
* @example
|
|
* Use with iterable.
|
|
* ```js
|
|
* var Promise = require('yaku');
|
|
* Promise.all((function * () {
|
|
* yield 10;
|
|
* yield new Promise(function (r) { setTimeout(r, 1000, "OK") });
|
|
* })())
|
|
* .then((values) => {
|
|
* console.log(values); // => [123, 0]
|
|
* });
|
|
* ```
|
|
*/
|
|
Yaku.all = function (iterable) {
|
|
var self = this
|
|
, p1 = newCapablePromise(self)
|
|
, res = []
|
|
, ret
|
|
;
|
|
|
|
function reject (reason) {
|
|
settlePromise(p1, $rejected, reason);
|
|
}
|
|
|
|
ret = genTryCatcher(each)(iterable, function (item, i) {
|
|
self.resolve(item).then(function (value) {
|
|
res[i] = value;
|
|
if (!--ret) settlePromise(p1, $resolved, res);
|
|
}, reject);
|
|
});
|
|
|
|
if (ret === $tryErr) return self.reject(ret.e);
|
|
|
|
if (!ret) settlePromise(p1, $resolved, []);
|
|
|
|
return p1;
|
|
};
|
|
|
|
/**
|
|
* The ES6 Symbol object that Yaku should use, by default it will use the
|
|
* global one.
|
|
* @type {Object}
|
|
* @example
|
|
* ```js
|
|
* var core = require("core-js/library");
|
|
* var Promise = require("yaku");
|
|
* Promise.Symbol = core.Symbol;
|
|
* ```
|
|
*/
|
|
Yaku.Symbol = root[$Symbol] || {};
|
|
|
|
// To support browsers that don't support `Object.defineProperty`.
|
|
genTryCatcher(function () {
|
|
Object.defineProperty(Yaku, getSpecies(), {
|
|
get: function () { return this; }
|
|
});
|
|
})();
|
|
|
|
/**
|
|
* Use this api to custom the species behavior.
|
|
* https://tc39.github.io/ecma262/#sec-speciesconstructor
|
|
* @param {Any} O The current this object.
|
|
* @param {Function} defaultConstructor
|
|
*/
|
|
Yaku.speciesConstructor = function (O, D) {
|
|
var C = O.constructor;
|
|
|
|
return C ? (C[getSpecies()] || D) : D;
|
|
};
|
|
|
|
/**
|
|
* Catch all possibly unhandled rejections. If you want to use specific
|
|
* format to display the error stack, overwrite it.
|
|
* If it is set, auto `console.error` unhandled rejection will be disabled.
|
|
* @param {Any} reason The rejection reason.
|
|
* @param {Yaku} p The promise that was rejected.
|
|
* @example
|
|
* ```js
|
|
* var Promise = require('yaku');
|
|
* Promise.unhandledRejection = (reason) => {
|
|
* console.error(reason);
|
|
* };
|
|
*
|
|
* // The console will log an unhandled rejection error message.
|
|
* Promise.reject('my reason');
|
|
*
|
|
* // The below won't log the unhandled rejection error message.
|
|
* Promise.reject('v')["catch"](() => {});
|
|
* ```
|
|
*/
|
|
Yaku.unhandledRejection = function (reason, p) {
|
|
console && console.error(
|
|
$unhandledRejectionMsg,
|
|
isLongStackTrace ? p.longStack : genStackInfo(reason, p)
|
|
);
|
|
};
|
|
|
|
/**
|
|
* Emitted whenever a Promise was rejected and an error handler was
|
|
* attached to it (for example with `["catch"]()`) later than after an event loop turn.
|
|
* @param {Any} reason The rejection reason.
|
|
* @param {Yaku} p The promise that was rejected.
|
|
*/
|
|
Yaku.rejectionHandled = $noop;
|
|
|
|
/**
|
|
* It is used to enable the long stack trace.
|
|
* Once it is enabled, it can't be reverted.
|
|
* While it is very helpful in development and testing environments,
|
|
* it is not recommended to use it in production. It will slow down
|
|
* application and eat up memory.
|
|
* It will add an extra property `longStack` to the Error object.
|
|
* @example
|
|
* ```js
|
|
* var Promise = require('yaku');
|
|
* Promise.enableLongStackTrace();
|
|
* Promise.reject(new Error("err"))["catch"]((err) => {
|
|
* console.log(err.longStack);
|
|
* });
|
|
* ```
|
|
*/
|
|
Yaku.enableLongStackTrace = function () {
|
|
isLongStackTrace = true;
|
|
};
|
|
|
|
/**
|
|
* Only Node has `process.nextTick` function. For browser there are
|
|
* so many ways to polyfill it. Yaku won't do it for you, instead you
|
|
* can choose what you prefer. For example, this project
|
|
* [next-tick](https://github.com/medikoo/next-tick).
|
|
* By default, Yaku will use `process.nextTick` on Node, `setTimeout` on browser.
|
|
* @type {Function}
|
|
* @example
|
|
* ```js
|
|
* var Promise = require('yaku');
|
|
* Promise.nextTick = require('next-tick');
|
|
* ```
|
|
* @example
|
|
* You can even use sync resolution if you really know what you are doing.
|
|
* ```js
|
|
* var Promise = require('yaku');
|
|
* Promise.nextTick = fn => fn();
|
|
* ```
|
|
*/
|
|
Yaku.nextTick = isBrowser ?
|
|
function (fn) {
|
|
nativePromise ?
|
|
new nativePromise(function (resolve) { resolve(); }).then(fn) :
|
|
setTimeout(fn);
|
|
} :
|
|
process.nextTick;
|
|
|
|
// ********************** Private **********************
|
|
|
|
Yaku._s = 1;
|
|
|
|
/**
|
|
* All static variable name will begin with `$`. Such as `$rejected`.
|
|
* @private
|
|
*/
|
|
|
|
// ******************************* Utils ********************************
|
|
|
|
function getSpecies () {
|
|
return Yaku[$Symbol][$species] || $speciesKey;
|
|
}
|
|
|
|
function extend (src, target) {
|
|
for (var k in target) {
|
|
src[k] = target[k];
|
|
}
|
|
}
|
|
|
|
function isObject (obj) {
|
|
return obj && typeof obj === 'object';
|
|
}
|
|
|
|
function isFunction (obj) {
|
|
return typeof obj === 'function';
|
|
}
|
|
|
|
function isInstanceOf (a, b) {
|
|
return a instanceof b;
|
|
}
|
|
|
|
function isError (obj) {
|
|
return isInstanceOf(obj, Err);
|
|
}
|
|
|
|
function ensureType (obj, fn, msg) {
|
|
if (!fn(obj)) throw genTypeError(msg);
|
|
}
|
|
|
|
/**
|
|
* Wrap a function into a try-catch.
|
|
* @private
|
|
* @return {Any | $tryErr}
|
|
*/
|
|
function tryCatcher () {
|
|
try {
|
|
return $tryCatchFn.apply($tryCatchThis, arguments);
|
|
} catch (e) {
|
|
$tryErr.e = e;
|
|
return $tryErr;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate a try-catch wrapped function.
|
|
* @private
|
|
* @param {Function} fn
|
|
* @return {Function}
|
|
*/
|
|
function genTryCatcher (fn, self) {
|
|
$tryCatchFn = fn;
|
|
$tryCatchThis = self;
|
|
return tryCatcher;
|
|
}
|
|
|
|
/**
|
|
* Generate a scheduler.
|
|
* @private
|
|
* @param {Integer} initQueueSize
|
|
* @param {Function} fn `(Yaku, Value) ->` The schedule handler.
|
|
* @return {Function} `(Yaku, Value) ->` The scheduler.
|
|
*/
|
|
function genScheduler (initQueueSize, fn) {
|
|
/**
|
|
* All async promise will be scheduled in
|
|
* here, so that they can be execute on the next tick.
|
|
* @private
|
|
*/
|
|
var fnQueue = Arr(initQueueSize)
|
|
, fnQueueLen = 0;
|
|
|
|
/**
|
|
* Run all queued functions.
|
|
* @private
|
|
*/
|
|
function flush () {
|
|
var i = 0;
|
|
while (i < fnQueueLen) {
|
|
fn(fnQueue[i], fnQueue[i + 1]);
|
|
fnQueue[i++] = $undefined;
|
|
fnQueue[i++] = $undefined;
|
|
}
|
|
|
|
fnQueueLen = 0;
|
|
if (fnQueue.length > initQueueSize) fnQueue.length = initQueueSize;
|
|
}
|
|
|
|
return function (v, arg) {
|
|
fnQueue[fnQueueLen++] = v;
|
|
fnQueue[fnQueueLen++] = arg;
|
|
|
|
if (fnQueueLen === 2) Yaku.nextTick(flush);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Generate a iterator
|
|
* @param {Any} obj
|
|
* @private
|
|
* @return {Object || TypeError}
|
|
*/
|
|
function each (iterable, fn) {
|
|
var len
|
|
, i = 0
|
|
, iter
|
|
, item
|
|
, ret
|
|
;
|
|
|
|
if (!iterable) throw genTypeError($invalidArgument);
|
|
|
|
var gen = iterable[Yaku[$Symbol][$iterator]];
|
|
if (isFunction(gen))
|
|
iter = gen.call(iterable);
|
|
else if (isFunction(iterable.next)) {
|
|
iter = iterable;
|
|
}
|
|
else if (isInstanceOf(iterable, Arr)) {
|
|
len = iterable.length;
|
|
while (i < len) {
|
|
fn(iterable[i], i++);
|
|
}
|
|
return i;
|
|
} else
|
|
throw genTypeError($invalidArgument);
|
|
|
|
while (!(item = iter.next()).done) {
|
|
ret = genTryCatcher(fn)(item.value, i++);
|
|
if (ret === $tryErr) {
|
|
isFunction(iter[$return]) && iter[$return]();
|
|
throw ret.e;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
/**
|
|
* Generate type error object.
|
|
* @private
|
|
* @param {String} msg
|
|
* @return {TypeError}
|
|
*/
|
|
function genTypeError (msg) {
|
|
return new TypeError(msg);
|
|
}
|
|
|
|
function genTraceInfo (noTitle) {
|
|
return (noTitle ? '' : $fromPrevious) + new Err().stack;
|
|
}
|
|
|
|
|
|
// *************************** Promise Helpers ****************************
|
|
|
|
/**
|
|
* Resolve the value returned by onFulfilled or onRejected.
|
|
* @private
|
|
* @param {Yaku} p1
|
|
* @param {Yaku} p2
|
|
*/
|
|
var scheduleHandler = genScheduler(999, function (p1, p2) {
|
|
var x, handler;
|
|
|
|
// 2.2.2
|
|
// 2.2.3
|
|
handler = p1._s !== $rejected ? p2._onFulfilled : p2._onRejected;
|
|
|
|
// 2.2.7.3
|
|
// 2.2.7.4
|
|
if (handler === $undefined) {
|
|
settlePromise(p2, p1._s, p1._v);
|
|
return;
|
|
}
|
|
|
|
// 2.2.7.1
|
|
x = genTryCatcher(callHanler)(handler, p1._v);
|
|
if (x === $tryErr) {
|
|
// 2.2.7.2
|
|
settlePromise(p2, $rejected, x.e);
|
|
return;
|
|
}
|
|
|
|
settleWithX(p2, x);
|
|
});
|
|
|
|
var scheduleUnhandledRejection = genScheduler(9, function (p) {
|
|
if (!hashOnRejected(p)) {
|
|
p[$unhandled] = 1;
|
|
emitEvent($unhandledRejection, p);
|
|
}
|
|
});
|
|
|
|
function emitEvent (name, p) {
|
|
var browserEventName = 'on' + name.toLowerCase()
|
|
, browserHandler = root[browserEventName];
|
|
|
|
if (process && process.listeners(name).length)
|
|
name === $unhandledRejection ?
|
|
process.emit(name, p._v, p) : process.emit(name, p);
|
|
else if (browserHandler)
|
|
browserHandler({ reason: p._v, promise: p });
|
|
else
|
|
Yaku[name](p._v, p);
|
|
}
|
|
|
|
function isYaku (val) { return val && val._s; }
|
|
|
|
function newCapablePromise (Constructor) {
|
|
if (isYaku(Constructor)) return new Constructor($noop);
|
|
|
|
var p, r, j;
|
|
p = new Constructor(function (resolve, reject) {
|
|
if (p) throw genTypeError();
|
|
|
|
r = resolve;
|
|
j = reject;
|
|
});
|
|
|
|
ensureType(r, isFunction);
|
|
ensureType(j, isFunction);
|
|
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* It will produce a settlePromise function to user.
|
|
* Such as the resolve and reject in this `new Yaku (resolve, reject) ->`.
|
|
* @private
|
|
* @param {Yaku} self
|
|
* @param {Integer} state The value is one of `$pending`, `$resolved` or `$rejected`.
|
|
* @return {Function} `(value) -> undefined` A resolve or reject function.
|
|
*/
|
|
function genSettler (self, state) {
|
|
var isCalled = false;
|
|
return function (value) {
|
|
if (isCalled) return;
|
|
isCalled = true;
|
|
|
|
if (isLongStackTrace)
|
|
self[$settlerTrace] = genTraceInfo(true);
|
|
|
|
if (state === $resolved)
|
|
settleWithX(self, value);
|
|
else
|
|
settlePromise(self, state, value);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Link the promise1 to the promise2.
|
|
* @private
|
|
* @param {Yaku} p1
|
|
* @param {Yaku} p2
|
|
* @param {Function} onFulfilled
|
|
* @param {Function} onRejected
|
|
*/
|
|
function addHandler (p1, p2, onFulfilled, onRejected) {
|
|
// 2.2.1
|
|
if (isFunction(onFulfilled))
|
|
p2._onFulfilled = onFulfilled;
|
|
if (isFunction(onRejected)) {
|
|
if (p1[$unhandled]) emitEvent($rejectionHandled, p1);
|
|
|
|
p2._onRejected = onRejected;
|
|
}
|
|
|
|
if (isLongStackTrace) p2._p = p1;
|
|
p1[p1._c++] = p2;
|
|
|
|
// 2.2.6
|
|
if (p1._s !== $pending)
|
|
scheduleHandler(p1, p2);
|
|
|
|
// 2.2.7
|
|
return p2;
|
|
}
|
|
|
|
// iterate tree
|
|
function hashOnRejected (node) {
|
|
// A node shouldn't be checked twice.
|
|
if (node._umark)
|
|
return true;
|
|
else
|
|
node._umark = true;
|
|
|
|
var i = 0
|
|
, len = node._c
|
|
, child;
|
|
|
|
while (i < len) {
|
|
child = node[i++];
|
|
if (child._onRejected || hashOnRejected(child)) return true;
|
|
}
|
|
}
|
|
|
|
function genStackInfo (reason, p) {
|
|
var stackInfo = [];
|
|
|
|
function push (trace) {
|
|
return stackInfo.push(trace.replace(/^\s+|\s+$/g, ''));
|
|
}
|
|
|
|
if (isLongStackTrace) {
|
|
if (p[$settlerTrace])
|
|
push(p[$settlerTrace]);
|
|
|
|
// Hope you guys could understand how the back trace works.
|
|
// We only have to iterate through the tree from the bottom to root.
|
|
(function iter (node) {
|
|
if (node && $promiseTrace in node) {
|
|
iter(node._next);
|
|
push(node[$promiseTrace] + '');
|
|
iter(node._p);
|
|
}
|
|
})(p);
|
|
}
|
|
|
|
return (reason && reason.stack ? reason.stack : reason) +
|
|
('\n' + stackInfo.join('\n')).replace($cleanStackReg, '');
|
|
}
|
|
|
|
function callHanler (handler, value) {
|
|
// 2.2.5
|
|
return handler(value);
|
|
}
|
|
|
|
/**
|
|
* Resolve or reject a promise.
|
|
* @private
|
|
* @param {Yaku} p
|
|
* @param {Integer} state
|
|
* @param {Any} value
|
|
*/
|
|
function settlePromise (p, state, value) {
|
|
var i = 0
|
|
, len = p._c;
|
|
|
|
// 2.1.2
|
|
// 2.1.3
|
|
if (p._s === $pending) {
|
|
// 2.1.1.1
|
|
p._s = state;
|
|
p._v = value;
|
|
|
|
if (state === $rejected) {
|
|
if (isLongStackTrace && isError(value)) {
|
|
value.longStack = genStackInfo(value, p);
|
|
}
|
|
|
|
scheduleUnhandledRejection(p);
|
|
}
|
|
|
|
// 2.2.4
|
|
while (i < len) {
|
|
scheduleHandler(p, p[i++]);
|
|
}
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* Resolve or reject promise with value x. The x can also be a thenable.
|
|
* @private
|
|
* @param {Yaku} p
|
|
* @param {Any | Thenable} x A normal value or a thenable.
|
|
*/
|
|
function settleWithX (p, x) {
|
|
// 2.3.1
|
|
if (x === p && x) {
|
|
settlePromise(p, $rejected, genTypeError($promiseCircularChain));
|
|
return p;
|
|
}
|
|
|
|
// 2.3.2
|
|
// 2.3.3
|
|
if (x !== $null && (isFunction(x) || isObject(x))) {
|
|
// 2.3.2.1
|
|
var xthen = genTryCatcher(getThen)(x);
|
|
|
|
if (xthen === $tryErr) {
|
|
// 2.3.3.2
|
|
settlePromise(p, $rejected, xthen.e);
|
|
return p;
|
|
}
|
|
|
|
if (isFunction(xthen)) {
|
|
if (isLongStackTrace && isYaku(x))
|
|
p._next = x;
|
|
|
|
// Fix https://bugs.chromium.org/p/v8/issues/detail?id=4162
|
|
if (isYaku(x))
|
|
settleXthen(p, x, xthen);
|
|
else
|
|
Yaku.nextTick(function () {
|
|
settleXthen(p, x, xthen);
|
|
});
|
|
} else
|
|
// 2.3.3.4
|
|
settlePromise(p, $resolved, x);
|
|
} else
|
|
// 2.3.4
|
|
settlePromise(p, $resolved, x);
|
|
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* Try to get a promise's then method.
|
|
* @private
|
|
* @param {Thenable} x
|
|
* @return {Function}
|
|
*/
|
|
function getThen (x) { return x.then; }
|
|
|
|
/**
|
|
* Resolve then with its promise.
|
|
* @private
|
|
* @param {Yaku} p
|
|
* @param {Thenable} x
|
|
* @param {Function} xthen
|
|
*/
|
|
function settleXthen (p, x, xthen) {
|
|
// 2.3.3.3
|
|
var err = genTryCatcher(xthen, x)(function (y) {
|
|
// 2.3.3.3.3
|
|
// 2.3.3.3.1
|
|
x && (x = $null, settleWithX(p, y));
|
|
}, function (r) {
|
|
// 2.3.3.3.3
|
|
// 2.3.3.3.2
|
|
x && (x = $null, settlePromise(p, $rejected, r));
|
|
});
|
|
|
|
// 2.3.3.3.4.1
|
|
if (err === $tryErr && x) {
|
|
// 2.3.3.3.4.2
|
|
settlePromise(p, $rejected, err.e);
|
|
x = $null;
|
|
}
|
|
}
|
|
|
|
root.Promise = Yaku;
|
|
})();
|