generators.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. "use strict";
  2. module.exports = function(Promise,
  3. apiRejection,
  4. INTERNAL,
  5. tryConvertToPromise,
  6. Proxyable,
  7. debug) {
  8. var errors = require("./errors");
  9. var TypeError = errors.TypeError;
  10. var util = require("./util");
  11. var errorObj = util.errorObj;
  12. var tryCatch = util.tryCatch;
  13. var yieldHandlers = [];
  14. function promiseFromYieldHandler(value, yieldHandlers, traceParent) {
  15. for (var i = 0; i < yieldHandlers.length; ++i) {
  16. traceParent._pushContext();
  17. var result = tryCatch(yieldHandlers[i])(value);
  18. traceParent._popContext();
  19. if (result === errorObj) {
  20. traceParent._pushContext();
  21. var ret = Promise.reject(errorObj.e);
  22. traceParent._popContext();
  23. return ret;
  24. }
  25. var maybePromise = tryConvertToPromise(result, traceParent);
  26. if (maybePromise instanceof Promise) return maybePromise;
  27. }
  28. return null;
  29. }
  30. function PromiseSpawn(generatorFunction, receiver, yieldHandler, stack) {
  31. var promise = this._promise = new Promise(INTERNAL);
  32. promise._captureStackTrace();
  33. promise._setOnCancel(this);
  34. this._stack = stack;
  35. this._generatorFunction = generatorFunction;
  36. this._receiver = receiver;
  37. this._generator = undefined;
  38. this._yieldHandlers = typeof yieldHandler === "function"
  39. ? [yieldHandler].concat(yieldHandlers)
  40. : yieldHandlers;
  41. this._yieldedPromise = null;
  42. }
  43. util.inherits(PromiseSpawn, Proxyable);
  44. PromiseSpawn.prototype._isResolved = function() {
  45. return this._promise === null;
  46. };
  47. PromiseSpawn.prototype._cleanup = function() {
  48. this._promise = this._generator = null;
  49. };
  50. PromiseSpawn.prototype._promiseCancelled = function() {
  51. if (this._isResolved()) return;
  52. var implementsReturn = typeof this._generator["return"] !== "undefined";
  53. var result;
  54. if (!implementsReturn) {
  55. var reason = new Promise.CancellationError(
  56. "generator .return() sentinel");
  57. Promise.coroutine.returnSentinel = reason;
  58. this._promise._attachExtraTrace(reason);
  59. this._promise._pushContext();
  60. result = tryCatch(this._generator["throw"]).call(this._generator,
  61. reason);
  62. this._promise._popContext();
  63. if (result === errorObj && result.e === reason) {
  64. result = null;
  65. }
  66. } else {
  67. this._promise._pushContext();
  68. result = tryCatch(this._generator["return"]).call(this._generator,
  69. undefined);
  70. this._promise._popContext();
  71. }
  72. var promise = this._promise;
  73. this._cleanup();
  74. if (result === errorObj) {
  75. promise._rejectCallback(result.e, false);
  76. } else {
  77. promise.cancel();
  78. }
  79. };
  80. PromiseSpawn.prototype._promiseFulfilled = function(value) {
  81. this._yieldedPromise = null;
  82. this._promise._pushContext();
  83. var result = tryCatch(this._generator.next).call(this._generator, value);
  84. this._promise._popContext();
  85. this._continue(result);
  86. };
  87. PromiseSpawn.prototype._promiseRejected = function(reason) {
  88. this._yieldedPromise = null;
  89. this._promise._attachExtraTrace(reason);
  90. this._promise._pushContext();
  91. var result = tryCatch(this._generator["throw"])
  92. .call(this._generator, reason);
  93. this._promise._popContext();
  94. this._continue(result);
  95. };
  96. PromiseSpawn.prototype._resultCancelled = function() {
  97. if (this._yieldedPromise instanceof Promise) {
  98. var promise = this._yieldedPromise;
  99. this._yieldedPromise = null;
  100. promise.cancel();
  101. }
  102. };
  103. PromiseSpawn.prototype.promise = function () {
  104. return this._promise;
  105. };
  106. PromiseSpawn.prototype._run = function () {
  107. this._generator = this._generatorFunction.call(this._receiver);
  108. this._receiver =
  109. this._generatorFunction = undefined;
  110. this._promiseFulfilled(undefined);
  111. };
  112. PromiseSpawn.prototype._continue = function (result) {
  113. var promise = this._promise;
  114. if (result === errorObj) {
  115. this._cleanup();
  116. return promise._rejectCallback(result.e, false);
  117. }
  118. var value = result.value;
  119. if (result.done === true) {
  120. this._cleanup();
  121. return promise._resolveCallback(value);
  122. } else {
  123. var maybePromise = tryConvertToPromise(value, this._promise);
  124. if (!(maybePromise instanceof Promise)) {
  125. maybePromise =
  126. promiseFromYieldHandler(maybePromise,
  127. this._yieldHandlers,
  128. this._promise);
  129. if (maybePromise === null) {
  130. this._promiseRejected(
  131. new TypeError(
  132. "A value %s was yielded that could not be treated as a promise\u000a\u000a See http://goo.gl/MqrFmX\u000a\u000a".replace("%s", value) +
  133. "From coroutine:\u000a" +
  134. this._stack.split("\n").slice(1, -7).join("\n")
  135. )
  136. );
  137. return;
  138. }
  139. }
  140. maybePromise = maybePromise._target();
  141. var bitField = maybePromise._bitField;
  142. ;
  143. if (((bitField & 50397184) === 0)) {
  144. this._yieldedPromise = maybePromise;
  145. maybePromise._proxy(this, null);
  146. } else if (((bitField & 33554432) !== 0)) {
  147. this._promiseFulfilled(maybePromise._value());
  148. } else if (((bitField & 16777216) !== 0)) {
  149. this._promiseRejected(maybePromise._reason());
  150. } else {
  151. this._promiseCancelled();
  152. }
  153. }
  154. };
  155. Promise.coroutine = function (generatorFunction, options) {
  156. if (typeof generatorFunction !== "function") {
  157. throw new TypeError("generatorFunction must be a function\u000a\u000a See http://goo.gl/MqrFmX\u000a");
  158. }
  159. var yieldHandler = Object(options).yieldHandler;
  160. var PromiseSpawn$ = PromiseSpawn;
  161. var stack = new Error().stack;
  162. return function () {
  163. var generator = generatorFunction.apply(this, arguments);
  164. var spawn = new PromiseSpawn$(undefined, undefined, yieldHandler,
  165. stack);
  166. var ret = spawn.promise();
  167. spawn._generator = generator;
  168. spawn._promiseFulfilled(undefined);
  169. return ret;
  170. };
  171. };
  172. Promise.coroutine.addYieldHandler = function(fn) {
  173. if (typeof fn !== "function") {
  174. throw new TypeError("expecting a function but got " + util.classString(fn));
  175. }
  176. yieldHandlers.push(fn);
  177. };
  178. Promise.spawn = function (generatorFunction) {
  179. debug.deprecated("Promise.spawn()", "Promise.coroutine()");
  180. if (typeof generatorFunction !== "function") {
  181. return apiRejection("generatorFunction must be a function\u000a\u000a See http://goo.gl/MqrFmX\u000a");
  182. }
  183. var spawn = new PromiseSpawn(generatorFunction, this);
  184. var ret = spawn.promise();
  185. spawn._run(Promise.spawn);
  186. return ret;
  187. };
  188. };