-internal.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import {
  2. objectOrFunction,
  3. isFunction
  4. } from './utils';
  5. import originalThen from './then';
  6. import originalResolve from './promise/resolve';
  7. import instrument from './instrument';
  8. import { config } from './config';
  9. import Promise from './promise';
  10. function withOwnPromise() {
  11. return new TypeError('A promises callback cannot return that same promise.');
  12. }
  13. export function noop() {}
  14. export const PENDING = void 0;
  15. export const FULFILLED = 1;
  16. export const REJECTED = 2;
  17. const GET_THEN_ERROR = new ErrorObject();
  18. export function getThen(promise) {
  19. try {
  20. return promise.then;
  21. } catch(error) {
  22. GET_THEN_ERROR.error = error;
  23. return GET_THEN_ERROR;
  24. }
  25. }
  26. function tryThen(then, value, fulfillmentHandler, rejectionHandler) {
  27. try {
  28. then.call(value, fulfillmentHandler, rejectionHandler);
  29. } catch(e) {
  30. return e;
  31. }
  32. }
  33. function handleForeignThenable(promise, thenable, then) {
  34. config.async(promise => {
  35. let sealed = false;
  36. let error = tryThen(then, thenable, value => {
  37. if (sealed) { return; }
  38. sealed = true;
  39. if (thenable !== value) {
  40. resolve(promise, value, undefined);
  41. } else {
  42. fulfill(promise, value);
  43. }
  44. }, reason => {
  45. if (sealed) { return; }
  46. sealed = true;
  47. reject(promise, reason);
  48. }, 'Settle: ' + (promise._label || ' unknown promise'));
  49. if (!sealed && error) {
  50. sealed = true;
  51. reject(promise, error);
  52. }
  53. }, promise);
  54. }
  55. function handleOwnThenable(promise, thenable) {
  56. if (thenable._state === FULFILLED) {
  57. fulfill(promise, thenable._result);
  58. } else if (thenable._state === REJECTED) {
  59. thenable._onError = null;
  60. reject(promise, thenable._result);
  61. } else {
  62. subscribe(thenable, undefined, value => {
  63. if (thenable !== value) {
  64. resolve(promise, value, undefined);
  65. } else {
  66. fulfill(promise, value);
  67. }
  68. }, reason => reject(promise, reason));
  69. }
  70. }
  71. export function handleMaybeThenable(promise, maybeThenable, then) {
  72. let isOwnThenable =
  73. maybeThenable.constructor === promise.constructor &&
  74. then === originalThen &&
  75. promise.constructor.resolve === originalResolve;
  76. if (isOwnThenable) {
  77. handleOwnThenable(promise, maybeThenable);
  78. } else if (then === GET_THEN_ERROR) {
  79. reject(promise, GET_THEN_ERROR.error);
  80. GET_THEN_ERROR.error = null;
  81. } else if (isFunction(then)) {
  82. handleForeignThenable(promise, maybeThenable, then);
  83. } else {
  84. fulfill(promise, maybeThenable);
  85. }
  86. }
  87. export function resolve(promise, value) {
  88. if (promise === value) {
  89. fulfill(promise, value);
  90. } else if (objectOrFunction(value)) {
  91. handleMaybeThenable(promise, value, getThen(value));
  92. } else {
  93. fulfill(promise, value);
  94. }
  95. }
  96. export function publishRejection(promise) {
  97. if (promise._onError) {
  98. promise._onError(promise._result);
  99. }
  100. publish(promise);
  101. }
  102. export function fulfill(promise, value) {
  103. if (promise._state !== PENDING) { return; }
  104. promise._result = value;
  105. promise._state = FULFILLED;
  106. if (promise._subscribers.length === 0) {
  107. if (config.instrument) {
  108. instrument('fulfilled', promise);
  109. }
  110. } else {
  111. config.async(publish, promise);
  112. }
  113. }
  114. export function reject(promise, reason) {
  115. if (promise._state !== PENDING) { return; }
  116. promise._state = REJECTED;
  117. promise._result = reason;
  118. config.async(publishRejection, promise);
  119. }
  120. export function subscribe(parent, child, onFulfillment, onRejection) {
  121. let subscribers = parent._subscribers;
  122. let length = subscribers.length;
  123. parent._onError = null;
  124. subscribers[length] = child;
  125. subscribers[length + FULFILLED] = onFulfillment;
  126. subscribers[length + REJECTED] = onRejection;
  127. if (length === 0 && parent._state) {
  128. config.async(publish, parent);
  129. }
  130. }
  131. export function publish(promise) {
  132. let subscribers = promise._subscribers;
  133. let settled = promise._state;
  134. if (config.instrument) {
  135. instrument(settled === FULFILLED ? 'fulfilled' : 'rejected', promise);
  136. }
  137. if (subscribers.length === 0) { return; }
  138. let child, callback, result = promise._result;
  139. for (let i = 0; i < subscribers.length; i += 3) {
  140. child = subscribers[i];
  141. callback = subscribers[i + settled];
  142. if (child) {
  143. invokeCallback(settled, child, callback, result);
  144. } else {
  145. callback(result);
  146. }
  147. }
  148. promise._subscribers.length = 0;
  149. }
  150. function ErrorObject() {
  151. this.error = null;
  152. }
  153. const TRY_CATCH_ERROR = new ErrorObject();
  154. function tryCatch(callback, result) {
  155. try {
  156. return callback(result);
  157. } catch(e) {
  158. TRY_CATCH_ERROR.error = e;
  159. return TRY_CATCH_ERROR;
  160. }
  161. }
  162. export function invokeCallback(state, promise, callback, result) {
  163. let hasCallback = isFunction(callback);
  164. let value, error;
  165. if (hasCallback) {
  166. value = tryCatch(callback, result);
  167. if (value === TRY_CATCH_ERROR) {
  168. error = value.error;
  169. value.error = null; // release
  170. } else if (value === promise) {
  171. reject(promise, withOwnPromise());
  172. return;
  173. }
  174. } else {
  175. value = result;
  176. }
  177. if (promise._state !== PENDING) {
  178. // noop
  179. } else if (hasCallback && error === undefined) {
  180. resolve(promise, value);
  181. } else if (error !== undefined) {
  182. reject(promise, error);
  183. } else if (state === FULFILLED) {
  184. fulfill(promise, value);
  185. } else if (state === REJECTED) {
  186. reject(promise, value);
  187. }
  188. }
  189. export function initializePromise(promise, resolver) {
  190. let resolved = false;
  191. try {
  192. resolver(value => {
  193. if (resolved) { return; }
  194. resolved = true;
  195. resolve(promise, value);
  196. }, reason => {
  197. if (resolved) { return; }
  198. resolved = true;
  199. reject(promise, reason);
  200. });
  201. } catch(e) {
  202. reject(promise, e);
  203. }
  204. }