1 | /*
|
2 | Yaku v0.18.1
|
3 | (c) 2015 Yad Smood. http://ysmood.org
|
4 | License MIT
|
5 | */
|
6 | /*
|
7 | Yaku v0.17.9
|
8 | (c) 2015 Yad Smood. http://ysmood.org
|
9 | License MIT
|
10 | */
|
11 | (function () {
|
12 | ;
|
13 |
|
14 | var $undefined
|
15 | , $null = null
|
16 | , isBrowser = typeof window === "object"
|
17 | , root = isBrowser ? window : global
|
18 | , process = root.process
|
19 | , console = root.console
|
20 | , isLongStackTrace = false
|
21 | , Arr = Array
|
22 | , Err = Error
|
23 |
|
24 | , $rejected = 1
|
25 | , $resolved = 2
|
26 | , $pending = 3
|
27 |
|
28 | , $Symbol = "Symbol"
|
29 | , $iterator = "iterator"
|
30 | , $species = "species"
|
31 | , $speciesKey = $Symbol + "(" + $species + ")"
|
32 | , $return = "return"
|
33 |
|
34 | , $unhandled = "_uh"
|
35 | , $promiseTrace = "_pt"
|
36 | , $settlerTrace = "_st"
|
37 |
|
38 | , $invalidThis = "Invalid this"
|
39 | , $invalidArgument = "Invalid argument"
|
40 | , $fromPrevious = "\nFrom previous "
|
41 | , $promiseCircularChain = "Chaining cycle detected for promise"
|
42 | , $unhandledRejectionMsg = "Uncaught (in promise)"
|
43 | , $rejectionHandled = "rejectionHandled"
|
44 | , $unhandledRejection = "unhandledRejection"
|
45 |
|
46 | , $tryCatchFn
|
47 | , $tryCatchThis
|
48 | , $tryErr = { e: $null }
|
49 | , $noop = function () {}
|
50 | , $cleanStackReg = /^.+\/node_modules\/yaku\/.+\n?/mg
|
51 | ;
|
52 |
|
53 | /**
|
54 | * This class follows the [Promises/A+](https://promisesaplus.com) and
|
55 | * [ES6](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-promise-objects) spec
|
56 | * with some extra helpers.
|
57 | * @param {Function} executor Function object with two arguments resolve, reject.
|
58 | * The first argument fulfills the promise, the second argument rejects it.
|
59 | * We can call these functions, once our operation is completed.
|
60 | */
|
61 | var Yaku = function (executor) {
|
62 | var self = this,
|
63 | err;
|
64 |
|
65 | // "this._s" is the internal state of: pending, resolved or rejected
|
66 | // "this._v" is the internal value
|
67 |
|
68 | if (!isObject(self) || self._s !== $undefined)
|
69 | throw genTypeError($invalidThis);
|
70 |
|
71 | self._s = $pending;
|
72 |
|
73 | if (isLongStackTrace) self[$promiseTrace] = genTraceInfo();
|
74 |
|
75 | if (executor !== $noop) {
|
76 | if (!isFunction(executor))
|
77 | throw genTypeError($invalidArgument);
|
78 |
|
79 | err = genTryCatcher(executor)(
|
80 | genSettler(self, $resolved),
|
81 | genSettler(self, $rejected)
|
82 | );
|
83 |
|
84 | if (err === $tryErr)
|
85 | settlePromise(self, $rejected, err.e);
|
86 | }
|
87 | };
|
88 |
|
89 | Yaku["default"] = Yaku;
|
90 |
|
91 | extend(Yaku.prototype, {
|
92 | /**
|
93 | * Appends fulfillment and rejection handlers to the promise,
|
94 | * and returns a new promise resolving to the return value of the called handler.
|
95 | * @param {Function} onFulfilled Optional. Called when the Promise is resolved.
|
96 | * @param {Function} onRejected Optional. Called when the Promise is rejected.
|
97 | * @return {Yaku} It will return a new Yaku which will resolve or reject after
|
98 | * @example
|
99 | * the current Promise.
|
100 | * ```js
|
101 | * var Promise = require('yaku');
|
102 | * var p = Promise.resolve(10);
|
103 | *
|
104 | * p.then((v) => {
|
105 | * console.log(v);
|
106 | * });
|
107 | * ```
|
108 | */
|
109 | then: function (onFulfilled, onRejected) {
|
110 | if (this._s === undefined) throw genTypeError();
|
111 |
|
112 | return addHandler(
|
113 | this,
|
114 | newCapablePromise(Yaku.speciesConstructor(this, Yaku)),
|
115 | onFulfilled,
|
116 | onRejected
|
117 | );
|
118 | },
|
119 |
|
120 | /**
|
121 | * The `catch()` method returns a Promise and deals with rejected cases only.
|
122 | * It behaves the same as calling `Promise.prototype.then(undefined, onRejected)`.
|
123 | * @param {Function} onRejected A Function called when the Promise is rejected.
|
124 | * This function has one argument, the rejection reason.
|
125 | * @return {Yaku} A Promise that deals with rejected cases only.
|
126 | * @example
|
127 | * ```js
|
128 | * var Promise = require('yaku');
|
129 | * var p = Promise.reject(new Error("ERR"));
|
130 | *
|
131 | * p['catch']((v) => {
|
132 | * console.log(v);
|
133 | * });
|
134 | * ```
|
135 | */
|
136 | "catch": function (onRejected) {
|
137 | return this.then($undefined, onRejected);
|
138 | },
|
139 |
|
140 | /**
|
141 | * Register a callback to be invoked when a promise is settled (either fulfilled or rejected).
|
142 | * Similar with the try-catch-finally, it's often used for cleanup.
|
143 | * @param {Function} onFinally A Function called when the Promise is settled.
|
144 | * It will not receive any argument.
|
145 | * @return {Yaku} A Promise that will reject if onFinally throws an error or returns a rejected promise.
|
146 | * Else it will resolve previous promise's final state (either fulfilled or rejected).
|
147 | * @example
|
148 | * ```js
|
149 | * var Promise = require('yaku');
|
150 | * var p = Math.random() > 0.5 ? Promise.resolve() : Promise.reject();
|
151 | * p.finally(() => {
|
152 | * console.log('finally');
|
153 | * });
|
154 | * ```
|
155 | */
|
156 | "finally": function (onFinally) {
|
157 | return this.then(function (val) {
|
158 | return Yaku.resolve(onFinally()).then(function () {
|
159 | return val;
|
160 | });
|
161 | }, function (err) {
|
162 | return Yaku.resolve(onFinally()).then(function () {
|
163 | throw err;
|
164 | });
|
165 | });
|
166 | },
|
167 |
|
168 | // The number of current promises that attach to this Yaku instance.
|
169 | _c: 0,
|
170 |
|
171 | // The parent Yaku.
|
172 | _p: $null
|
173 | });
|
174 |
|
175 | /**
|
176 | * The `Promise.resolve(value)` method returns a Promise object that is resolved with the given value.
|
177 | * If the value is a thenable (i.e. has a then method), the returned promise will "follow" that thenable,
|
178 | * adopting its eventual state; otherwise the returned promise will be fulfilled with the value.
|
179 | * @param {Any} value Argument to be resolved by this Promise.
|
180 | * Can also be a Promise or a thenable to resolve.
|
181 | * @return {Yaku}
|
182 | * @example
|
183 | * ```js
|
184 | * var Promise = require('yaku');
|
185 | * var p = Promise.resolve(10);
|
186 | * ```
|
187 | */
|
188 | Yaku.resolve = function (val) {
|
189 | return isYaku(val) ? val : settleWithX(newCapablePromise(this), val);
|
190 | };
|
191 |
|
192 | /**
|
193 | * The `Promise.reject(reason)` method returns a Promise object that is rejected with the given reason.
|
194 | * @param {Any} reason Reason why this Promise rejected.
|
195 | * @return {Yaku}
|
196 | * @example
|
197 | * ```js
|
198 | * var Promise = require('yaku');
|
199 | * var p = Promise.reject(new Error("ERR"));
|
200 | * ```
|
201 | */
|
202 | Yaku.reject = function (reason) {
|
203 | return settlePromise(newCapablePromise(this), $rejected, reason);
|
204 | };
|
205 |
|
206 | /**
|
207 | * The `Promise.race(iterable)` method returns a promise that resolves or rejects
|
208 | * as soon as one of the promises in the iterable resolves or rejects,
|
209 | * with the value or reason from that promise.
|
210 | * @param {iterable} iterable An iterable object, such as an Array.
|
211 | * @return {Yaku} The race function returns a Promise that is settled
|
212 | * the same way as the first passed promise to settle.
|
213 | * It resolves or rejects, whichever happens first.
|
214 | * @example
|
215 | * ```js
|
216 | * var Promise = require('yaku');
|
217 | * Promise.race([
|
218 | * 123,
|
219 | * Promise.resolve(0)
|
220 | * ])
|
221 | * .then((value) => {
|
222 | * console.log(value); // => 123
|
223 | * });
|
224 | * ```
|
225 | */
|
226 | Yaku.race = function (iterable) {
|
227 | var self = this
|
228 | , p = newCapablePromise(self)
|
229 |
|
230 | , resolve = function (val) {
|
231 | settlePromise(p, $resolved, val);
|
232 | }
|
233 |
|
234 | , reject = function (val) {
|
235 | settlePromise(p, $rejected, val);
|
236 | }
|
237 |
|
238 | , ret = genTryCatcher(each)(iterable, function (v) {
|
239 | self.resolve(v).then(resolve, reject);
|
240 | });
|
241 |
|
242 | if (ret === $tryErr) return self.reject(ret.e);
|
243 |
|
244 | return p;
|
245 | };
|
246 |
|
247 | /**
|
248 | * The `Promise.all(iterable)` method returns a promise that resolves when
|
249 | * all of the promises in the iterable argument have resolved.
|
250 | *
|
251 | * The result is passed as an array of values from all the promises.
|
252 | * If something passed in the iterable array is not a promise,
|
253 | * it's converted to one by Promise.resolve. If any of the passed in promises rejects,
|
254 | * the all Promise immediately rejects with the value of the promise that rejected,
|
255 | * discarding all the other promises whether or not they have resolved.
|
256 | * @param {iterable} iterable An iterable object, such as an Array.
|
257 | * @return {Yaku}
|
258 | * @example
|
259 | * ```js
|
260 | * var Promise = require('yaku');
|
261 | * Promise.all([
|
262 | * 123,
|
263 | * Promise.resolve(0)
|
264 | * ])
|
265 | * .then((values) => {
|
266 | * console.log(values); // => [123, 0]
|
267 | * });
|
268 | * ```
|
269 | * @example
|
270 | * Use with iterable.
|
271 | * ```js
|
272 | * var Promise = require('yaku');
|
273 | * Promise.all((function * () {
|
274 | * yield 10;
|
275 | * yield new Promise(function (r) { setTimeout(r, 1000, "OK") });
|
276 | * })())
|
277 | * .then((values) => {
|
278 | * console.log(values); // => [123, 0]
|
279 | * });
|
280 | * ```
|
281 | */
|
282 | Yaku.all = function (iterable) {
|
283 | var self = this
|
284 | , p1 = newCapablePromise(self)
|
285 | , res = []
|
286 | , ret
|
287 | ;
|
288 |
|
289 | function reject (reason) {
|
290 | settlePromise(p1, $rejected, reason);
|
291 | }
|
292 |
|
293 | ret = genTryCatcher(each)(iterable, function (item, i) {
|
294 | self.resolve(item).then(function (value) {
|
295 | res[i] = value;
|
296 | if (!--ret) settlePromise(p1, $resolved, res);
|
297 | }, reject);
|
298 | });
|
299 |
|
300 | if (ret === $tryErr) return self.reject(ret.e);
|
301 |
|
302 | if (!ret) settlePromise(p1, $resolved, []);
|
303 |
|
304 | return p1;
|
305 | };
|
306 |
|
307 | /**
|
308 | * The ES6 Symbol object that Yaku should use, by default it will use the
|
309 | * global one.
|
310 | * @type {Object}
|
311 | * @example
|
312 | * ```js
|
313 | * var core = require("core-js/library");
|
314 | * var Promise = require("yaku");
|
315 | * Promise.Symbol = core.Symbol;
|
316 | * ```
|
317 | */
|
318 | Yaku.Symbol = root[$Symbol] || {};
|
319 |
|
320 | // To support browsers that don't support `Object.defineProperty`.
|
321 | genTryCatcher(function () {
|
322 | Object.defineProperty(Yaku, getSpecies(), {
|
323 | get: function () { return this; }
|
324 | });
|
325 | })();
|
326 |
|
327 | /**
|
328 | * Use this api to custom the species behavior.
|
329 | * https://tc39.github.io/ecma262/#sec-speciesconstructor
|
330 | * @param {Any} O The current this object.
|
331 | * @param {Function} defaultConstructor
|
332 | */
|
333 | Yaku.speciesConstructor = function (O, D) {
|
334 | var C = O.constructor;
|
335 |
|
336 | return C ? (C[getSpecies()] || D) : D;
|
337 | };
|
338 |
|
339 | /**
|
340 | * Catch all possibly unhandled rejections. If you want to use specific
|
341 | * format to display the error stack, overwrite it.
|
342 | * If it is set, auto `console.error` unhandled rejection will be disabled.
|
343 | * @param {Any} reason The rejection reason.
|
344 | * @param {Yaku} p The promise that was rejected.
|
345 | * @example
|
346 | * ```js
|
347 | * var Promise = require('yaku');
|
348 | * Promise.unhandledRejection = (reason) => {
|
349 | * console.error(reason);
|
350 | * };
|
351 | *
|
352 | * // The console will log an unhandled rejection error message.
|
353 | * Promise.reject('my reason');
|
354 | *
|
355 | * // The below won't log the unhandled rejection error message.
|
356 | * Promise.reject('v')["catch"](() => {});
|
357 | * ```
|
358 | */
|
359 | Yaku.unhandledRejection = function (reason, p) {
|
360 | console && console.error(
|
361 | $unhandledRejectionMsg,
|
362 | isLongStackTrace ? p.longStack : genStackInfo(reason, p)
|
363 | );
|
364 | };
|
365 |
|
366 | /**
|
367 | * Emitted whenever a Promise was rejected and an error handler was
|
368 | * attached to it (for example with `["catch"]()`) later than after an event loop turn.
|
369 | * @param {Any} reason The rejection reason.
|
370 | * @param {Yaku} p The promise that was rejected.
|
371 | */
|
372 | Yaku.rejectionHandled = $noop;
|
373 |
|
374 | /**
|
375 | * It is used to enable the long stack trace.
|
376 | * Once it is enabled, it can't be reverted.
|
377 | * While it is very helpful in development and testing environments,
|
378 | * it is not recommended to use it in production. It will slow down
|
379 | * application and eat up memory.
|
380 | * It will add an extra property `longStack` to the Error object.
|
381 | * @example
|
382 | * ```js
|
383 | * var Promise = require('yaku');
|
384 | * Promise.enableLongStackTrace();
|
385 | * Promise.reject(new Error("err"))["catch"]((err) => {
|
386 | * console.log(err.longStack);
|
387 | * });
|
388 | * ```
|
389 | */
|
390 | Yaku.enableLongStackTrace = function () {
|
391 | isLongStackTrace = true;
|
392 | };
|
393 |
|
394 | /**
|
395 | * Only Node has `process.nextTick` function. For browser there are
|
396 | * so many ways to polyfill it. Yaku won't do it for you, instead you
|
397 | * can choose what you prefer. For example, this project
|
398 | * [next-tick](https://github.com/medikoo/next-tick).
|
399 | * By default, Yaku will use `process.nextTick` on Node, `setTimeout` on browser.
|
400 | * @type {Function}
|
401 | * @example
|
402 | * ```js
|
403 | * var Promise = require('yaku');
|
404 | * Promise.nextTick = require('next-tick');
|
405 | * ```
|
406 | * @example
|
407 | * You can even use sync resolution if you really know what you are doing.
|
408 | * ```js
|
409 | * var Promise = require('yaku');
|
410 | * Promise.nextTick = fn => fn();
|
411 | * ```
|
412 | */
|
413 | Yaku.nextTick = isBrowser ?
|
414 | function (fn) { setTimeout(fn); } :
|
415 | process.nextTick;
|
416 |
|
417 | // ********************** Private **********************
|
418 |
|
419 | Yaku._s = 1;
|
420 |
|
421 | /**
|
422 | * All static variable name will begin with `$`. Such as `$rejected`.
|
423 | * @private
|
424 | */
|
425 |
|
426 | // ******************************* Utils ********************************
|
427 |
|
428 | function getSpecies () {
|
429 | return Yaku[$Symbol][$species] || $speciesKey;
|
430 | }
|
431 |
|
432 | function extend (src, target) {
|
433 | for (var k in target) {
|
434 | src[k] = target[k];
|
435 | }
|
436 | }
|
437 |
|
438 | function isObject (obj) {
|
439 | return obj && typeof obj === "object";
|
440 | }
|
441 |
|
442 | function isFunction (obj) {
|
443 | return typeof obj === "function";
|
444 | }
|
445 |
|
446 | function isInstanceOf (a, b) {
|
447 | return a instanceof b;
|
448 | }
|
449 |
|
450 | function isError (obj) {
|
451 | return isInstanceOf(obj, Err);
|
452 | }
|
453 |
|
454 | function ensureType (obj, fn, msg) {
|
455 | if (!fn(obj)) throw genTypeError(msg);
|
456 | }
|
457 |
|
458 | /**
|
459 | * Wrap a function into a try-catch.
|
460 | * @private
|
461 | * @return {Any | $tryErr}
|
462 | */
|
463 | function tryCatcher () {
|
464 | try {
|
465 | return $tryCatchFn.apply($tryCatchThis, arguments);
|
466 | } catch (e) {
|
467 | $tryErr.e = e;
|
468 | return $tryErr;
|
469 | }
|
470 | }
|
471 |
|
472 | /**
|
473 | * Generate a try-catch wrapped function.
|
474 | * @private
|
475 | * @param {Function} fn
|
476 | * @return {Function}
|
477 | */
|
478 | function genTryCatcher (fn, self) {
|
479 | $tryCatchFn = fn;
|
480 | $tryCatchThis = self;
|
481 | return tryCatcher;
|
482 | }
|
483 |
|
484 | /**
|
485 | * Generate a scheduler.
|
486 | * @private
|
487 | * @param {Integer} initQueueSize
|
488 | * @param {Function} fn `(Yaku, Value) ->` The schedule handler.
|
489 | * @return {Function} `(Yaku, Value) ->` The scheduler.
|
490 | */
|
491 | function genScheduler (initQueueSize, fn) {
|
492 | /**
|
493 | * All async promise will be scheduled in
|
494 | * here, so that they can be execute on the next tick.
|
495 | * @private
|
496 | */
|
497 | var fnQueue = Arr(initQueueSize)
|
498 | , fnQueueLen = 0;
|
499 |
|
500 | /**
|
501 | * Run all queued functions.
|
502 | * @private
|
503 | */
|
504 | function flush () {
|
505 | var i = 0;
|
506 | while (i < fnQueueLen) {
|
507 | fn(fnQueue[i], fnQueue[i + 1]);
|
508 | fnQueue[i++] = $undefined;
|
509 | fnQueue[i++] = $undefined;
|
510 | }
|
511 |
|
512 | fnQueueLen = 0;
|
513 | if (fnQueue.length > initQueueSize) fnQueue.length = initQueueSize;
|
514 | }
|
515 |
|
516 | return function (v, arg) {
|
517 | fnQueue[fnQueueLen++] = v;
|
518 | fnQueue[fnQueueLen++] = arg;
|
519 |
|
520 | if (fnQueueLen === 2) Yaku.nextTick(flush);
|
521 | };
|
522 | }
|
523 |
|
524 | /**
|
525 | * Generate a iterator
|
526 | * @param {Any} obj
|
527 | * @private
|
528 | * @return {Object || TypeError}
|
529 | */
|
530 | function each (iterable, fn) {
|
531 | var len
|
532 | , i = 0
|
533 | , iter
|
534 | , item
|
535 | , ret
|
536 | ;
|
537 |
|
538 | if (!iterable) throw genTypeError($invalidArgument);
|
539 |
|
540 | var gen = iterable[Yaku[$Symbol][$iterator]];
|
541 | if (isFunction(gen))
|
542 | iter = gen.call(iterable);
|
543 | else if (isFunction(iterable.next)) {
|
544 | iter = iterable;
|
545 | }
|
546 | else if (isInstanceOf(iterable, Arr)) {
|
547 | len = iterable.length;
|
548 | while (i < len) {
|
549 | fn(iterable[i], i++);
|
550 | }
|
551 | return i;
|
552 | } else
|
553 | throw genTypeError($invalidArgument);
|
554 |
|
555 | while (!(item = iter.next()).done) {
|
556 | ret = genTryCatcher(fn)(item.value, i++);
|
557 | if (ret === $tryErr) {
|
558 | isFunction(iter[$return]) && iter[$return]();
|
559 | throw ret.e;
|
560 | }
|
561 | }
|
562 |
|
563 | return i;
|
564 | }
|
565 |
|
566 | /**
|
567 | * Generate type error object.
|
568 | * @private
|
569 | * @param {String} msg
|
570 | * @return {TypeError}
|
571 | */
|
572 | function genTypeError (msg) {
|
573 | return new TypeError(msg);
|
574 | }
|
575 |
|
576 | function genTraceInfo (noTitle) {
|
577 | return (noTitle ? "" : $fromPrevious) + new Err().stack;
|
578 | }
|
579 |
|
580 |
|
581 | // *************************** Promise Helpers ****************************
|
582 |
|
583 | /**
|
584 | * Resolve the value returned by onFulfilled or onRejected.
|
585 | * @private
|
586 | * @param {Yaku} p1
|
587 | * @param {Yaku} p2
|
588 | */
|
589 | var scheduleHandler = genScheduler(999, function (p1, p2) {
|
590 | var x, handler;
|
591 |
|
592 | // 2.2.2
|
593 | // 2.2.3
|
594 | handler = p1._s !== $rejected ? p2._onFulfilled : p2._onRejected;
|
595 |
|
596 | // 2.2.7.3
|
597 | // 2.2.7.4
|
598 | if (handler === $undefined) {
|
599 | settlePromise(p2, p1._s, p1._v);
|
600 | return;
|
601 | }
|
602 |
|
603 | // 2.2.7.1
|
604 | x = genTryCatcher(callHanler)(handler, p1._v);
|
605 | if (x === $tryErr) {
|
606 | // 2.2.7.2
|
607 | settlePromise(p2, $rejected, x.e);
|
608 | return;
|
609 | }
|
610 |
|
611 | settleWithX(p2, x);
|
612 | });
|
613 |
|
614 | var scheduleUnhandledRejection = genScheduler(9, function (p) {
|
615 | if (!hashOnRejected(p)) {
|
616 | p[$unhandled] = 1;
|
617 | emitEvent($unhandledRejection, p);
|
618 | }
|
619 | });
|
620 |
|
621 | function emitEvent (name, p) {
|
622 | var browserEventName = "on" + name.toLowerCase()
|
623 | , browserHandler = root[browserEventName];
|
624 |
|
625 | if (process && process.listeners(name).length)
|
626 | name === $unhandledRejection ?
|
627 | process.emit(name, p._v, p) : process.emit(name, p);
|
628 | else if (browserHandler)
|
629 | browserHandler({ reason: p._v, promise: p });
|
630 | else
|
631 | Yaku[name](p._v, p);
|
632 | }
|
633 |
|
634 | function isYaku (val) { return val && val._s; }
|
635 |
|
636 | function newCapablePromise (Constructor) {
|
637 | if (isYaku(Constructor)) return new Constructor($noop);
|
638 |
|
639 | var p, r, j;
|
640 | p = new Constructor(function (resolve, reject) {
|
641 | if (p) throw genTypeError();
|
642 |
|
643 | r = resolve;
|
644 | j = reject;
|
645 | });
|
646 |
|
647 | ensureType(r, isFunction);
|
648 | ensureType(j, isFunction);
|
649 |
|
650 | return p;
|
651 | }
|
652 |
|
653 | /**
|
654 | * It will produce a settlePromise function to user.
|
655 | * Such as the resolve and reject in this `new Yaku (resolve, reject) ->`.
|
656 | * @private
|
657 | * @param {Yaku} self
|
658 | * @param {Integer} state The value is one of `$pending`, `$resolved` or `$rejected`.
|
659 | * @return {Function} `(value) -> undefined` A resolve or reject function.
|
660 | */
|
661 | function genSettler (self, state) {
|
662 | return function (value) {
|
663 | if (isLongStackTrace)
|
664 | self[$settlerTrace] = genTraceInfo(true);
|
665 |
|
666 | if (state === $resolved)
|
667 | settleWithX(self, value);
|
668 | else
|
669 | settlePromise(self, state, value);
|
670 | };
|
671 | }
|
672 |
|
673 | /**
|
674 | * Link the promise1 to the promise2.
|
675 | * @private
|
676 | * @param {Yaku} p1
|
677 | * @param {Yaku} p2
|
678 | * @param {Function} onFulfilled
|
679 | * @param {Function} onRejected
|
680 | */
|
681 | function addHandler (p1, p2, onFulfilled, onRejected) {
|
682 | // 2.2.1
|
683 | if (isFunction(onFulfilled))
|
684 | p2._onFulfilled = onFulfilled;
|
685 | if (isFunction(onRejected)) {
|
686 | if (p1[$unhandled]) emitEvent($rejectionHandled, p1);
|
687 |
|
688 | p2._onRejected = onRejected;
|
689 | }
|
690 |
|
691 | if (isLongStackTrace) p2._p = p1;
|
692 | p1[p1._c++] = p2;
|
693 |
|
694 | // 2.2.6
|
695 | if (p1._s !== $pending)
|
696 | scheduleHandler(p1, p2);
|
697 |
|
698 | // 2.2.7
|
699 | return p2;
|
700 | }
|
701 |
|
702 | // iterate tree
|
703 | function hashOnRejected (node) {
|
704 | // A node shouldn't be checked twice.
|
705 | if (node._umark)
|
706 | return true;
|
707 | else
|
708 | node._umark = true;
|
709 |
|
710 | var i = 0
|
711 | , len = node._c
|
712 | , child;
|
713 |
|
714 | while (i < len) {
|
715 | child = node[i++];
|
716 | if (child._onRejected || hashOnRejected(child)) return true;
|
717 | }
|
718 | }
|
719 |
|
720 | function genStackInfo (reason, p) {
|
721 | var stackInfo = [];
|
722 |
|
723 | function push (trace) {
|
724 | return stackInfo.push(trace.replace(/^\s+|\s+$/g, ""));
|
725 | }
|
726 |
|
727 | if (isLongStackTrace) {
|
728 | if (p[$settlerTrace])
|
729 | push(p[$settlerTrace]);
|
730 |
|
731 | // Hope you guys could understand how the back trace works.
|
732 | // We only have to iterate through the tree from the bottom to root.
|
733 | (function iter (node) {
|
734 | if (node && $promiseTrace in node) {
|
735 | iter(node._next);
|
736 | push(node[$promiseTrace] + "");
|
737 | iter(node._p);
|
738 | }
|
739 | })(p);
|
740 | }
|
741 |
|
742 | return (reason && reason.stack ? reason.stack : reason) +
|
743 | ("\n" + stackInfo.join("\n")).replace($cleanStackReg, "");
|
744 | }
|
745 |
|
746 | function callHanler (handler, value) {
|
747 | // 2.2.5
|
748 | return handler(value);
|
749 | }
|
750 |
|
751 | /**
|
752 | * Resolve or reject a promise.
|
753 | * @private
|
754 | * @param {Yaku} p
|
755 | * @param {Integer} state
|
756 | * @param {Any} value
|
757 | */
|
758 | function settlePromise (p, state, value) {
|
759 | var i = 0
|
760 | , len = p._c;
|
761 |
|
762 | // 2.1.2
|
763 | // 2.1.3
|
764 | if (p._s === $pending) {
|
765 | // 2.1.1.1
|
766 | p._s = state;
|
767 | p._v = value;
|
768 |
|
769 | if (state === $rejected) {
|
770 | if (isLongStackTrace && isError(value)) {
|
771 | value.longStack = genStackInfo(value, p);
|
772 | }
|
773 |
|
774 | scheduleUnhandledRejection(p);
|
775 | }
|
776 |
|
777 | // 2.2.4
|
778 | while (i < len) {
|
779 | scheduleHandler(p, p[i++]);
|
780 | }
|
781 | }
|
782 |
|
783 | return p;
|
784 | }
|
785 |
|
786 | /**
|
787 | * Resolve or reject promise with value x. The x can also be a thenable.
|
788 | * @private
|
789 | * @param {Yaku} p
|
790 | * @param {Any | Thenable} x A normal value or a thenable.
|
791 | */
|
792 | function settleWithX (p, x) {
|
793 | // 2.3.1
|
794 | if (x === p && x) {
|
795 | settlePromise(p, $rejected, genTypeError($promiseCircularChain));
|
796 | return p;
|
797 | }
|
798 |
|
799 | // 2.3.2
|
800 | // 2.3.3
|
801 | if (x !== $null && (isFunction(x) || isObject(x))) {
|
802 | // 2.3.2.1
|
803 | var xthen = genTryCatcher(getThen)(x);
|
804 |
|
805 | if (xthen === $tryErr) {
|
806 | // 2.3.3.2
|
807 | settlePromise(p, $rejected, xthen.e);
|
808 | return p;
|
809 | }
|
810 |
|
811 | if (isFunction(xthen)) {
|
812 | if (isLongStackTrace && isYaku(x))
|
813 | p._next = x;
|
814 |
|
815 | // Fix https://bugs.chromium.org/p/v8/issues/detail?id=4162
|
816 | if (isYaku(x))
|
817 | settleXthen(p, x, xthen);
|
818 | else
|
819 | Yaku.nextTick(function () {
|
820 | settleXthen(p, x, xthen);
|
821 | });
|
822 | } else
|
823 | // 2.3.3.4
|
824 | settlePromise(p, $resolved, x);
|
825 | } else
|
826 | // 2.3.4
|
827 | settlePromise(p, $resolved, x);
|
828 |
|
829 | return p;
|
830 | }
|
831 |
|
832 | /**
|
833 | * Try to get a promise's then method.
|
834 | * @private
|
835 | * @param {Thenable} x
|
836 | * @return {Function}
|
837 | */
|
838 | function getThen (x) { return x.then; }
|
839 |
|
840 | /**
|
841 | * Resolve then with its promise.
|
842 | * @private
|
843 | * @param {Yaku} p
|
844 | * @param {Thenable} x
|
845 | * @param {Function} xthen
|
846 | */
|
847 | function settleXthen (p, x, xthen) {
|
848 | // 2.3.3.3
|
849 | var err = genTryCatcher(xthen, x)(function (y) {
|
850 | // 2.3.3.3.3
|
851 | // 2.3.3.3.1
|
852 | x && (x = $null, settleWithX(p, y));
|
853 | }, function (r) {
|
854 | // 2.3.3.3.3
|
855 | // 2.3.3.3.2
|
856 | x && (x = $null, settlePromise(p, $rejected, r));
|
857 | });
|
858 |
|
859 | // 2.3.3.3.4.1
|
860 | if (err === $tryErr && x) {
|
861 | // 2.3.3.3.4.2
|
862 | settlePromise(p, $rejected, err.e);
|
863 | x = $null;
|
864 | }
|
865 | }
|
866 |
|
867 | try {
|
868 | module.exports = Yaku;
|
869 | } catch (e) {
|
870 | /* istanbul ignore next */
|
871 | root.Yaku = Yaku;
|
872 | }
|
873 | })();
|