yaku.js 24 KB

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