generators.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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. if (debug.cancellation()) {
  32. var internal = new Promise(INTERNAL);
  33. var _finallyPromise = this._finallyPromise = new Promise(INTERNAL);
  34. this._promise = internal.lastly(function() {
  35. return _finallyPromise;
  36. });
  37. internal._captureStackTrace();
  38. internal._setOnCancel(this);
  39. } else {
  40. var promise = this._promise = new Promise(INTERNAL);
  41. promise._captureStackTrace();
  42. }
  43. this._stack = stack;
  44. this._generatorFunction = generatorFunction;
  45. this._receiver = receiver;
  46. this._generator = undefined;
  47. this._yieldHandlers = typeof yieldHandler === "function"
  48. ? [yieldHandler].concat(yieldHandlers)
  49. : yieldHandlers;
  50. this._yieldedPromise = null;
  51. this._cancellationPhase = false;
  52. }
  53. util.inherits(PromiseSpawn, Proxyable);
  54. PromiseSpawn.prototype._isResolved = function() {
  55. return this._promise === null;
  56. };
  57. PromiseSpawn.prototype._cleanup = function() {
  58. this._promise = this._generator = null;
  59. if (debug.cancellation() && this._finallyPromise !== null) {
  60. this._finallyPromise._fulfill();
  61. this._finallyPromise = null;
  62. }
  63. };
  64. PromiseSpawn.prototype._promiseCancelled = function() {
  65. if (this._isResolved()) return;
  66. var implementsReturn = typeof this._generator["return"] !== "undefined";
  67. var result;
  68. if (!implementsReturn) {
  69. var reason = new Promise.CancellationError(
  70. "generator .return() sentinel");
  71. Promise.coroutine.returnSentinel = reason;
  72. this._promise._attachExtraTrace(reason);
  73. this._promise._pushContext();
  74. result = tryCatch(this._generator["throw"]).call(this._generator,
  75. reason);
  76. this._promise._popContext();
  77. } else {
  78. this._promise._pushContext();
  79. result = tryCatch(this._generator["return"]).call(this._generator,
  80. undefined);
  81. this._promise._popContext();
  82. }
  83. this._cancellationPhase = true;
  84. this._yieldedPromise = null;
  85. this._continue(result);
  86. };
  87. PromiseSpawn.prototype._promiseFulfilled = function(value) {
  88. this._yieldedPromise = null;
  89. this._promise._pushContext();
  90. var result = tryCatch(this._generator.next).call(this._generator, value);
  91. this._promise._popContext();
  92. this._continue(result);
  93. };
  94. PromiseSpawn.prototype._promiseRejected = function(reason) {
  95. this._yieldedPromise = null;
  96. this._promise._attachExtraTrace(reason);
  97. this._promise._pushContext();
  98. var result = tryCatch(this._generator["throw"])
  99. .call(this._generator, reason);
  100. this._promise._popContext();
  101. this._continue(result);
  102. };
  103. PromiseSpawn.prototype._resultCancelled = function() {
  104. if (this._yieldedPromise instanceof Promise) {
  105. var promise = this._yieldedPromise;
  106. this._yieldedPromise = null;
  107. promise.cancel();
  108. }
  109. };
  110. PromiseSpawn.prototype.promise = function () {
  111. return this._promise;
  112. };
  113. PromiseSpawn.prototype._run = function () {
  114. this._generator = this._generatorFunction.call(this._receiver);
  115. this._receiver =
  116. this._generatorFunction = undefined;
  117. this._promiseFulfilled(undefined);
  118. };
  119. PromiseSpawn.prototype._continue = function (result) {
  120. var promise = this._promise;
  121. if (result === errorObj) {
  122. this._cleanup();
  123. if (this._cancellationPhase) {
  124. return promise.cancel();
  125. } else {
  126. return promise._rejectCallback(result.e, false);
  127. }
  128. }
  129. var value = result.value;
  130. if (result.done === true) {
  131. this._cleanup();
  132. if (this._cancellationPhase) {
  133. return promise.cancel();
  134. } else {
  135. return promise._resolveCallback(value);
  136. }
  137. } else {
  138. var maybePromise = tryConvertToPromise(value, this._promise);
  139. if (!(maybePromise instanceof Promise)) {
  140. maybePromise =
  141. promiseFromYieldHandler(maybePromise,
  142. this._yieldHandlers,
  143. this._promise);
  144. if (maybePromise === null) {
  145. this._promiseRejected(
  146. new TypeError(
  147. "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) +
  148. "From coroutine:\u000a" +
  149. this._stack.split("\n").slice(1, -7).join("\n")
  150. )
  151. );
  152. return;
  153. }
  154. }
  155. maybePromise = maybePromise._target();
  156. var bitField = maybePromise._bitField;
  157. ;
  158. if (((bitField & 50397184) === 0)) {
  159. this._yieldedPromise = maybePromise;
  160. maybePromise._proxy(this, null);
  161. } else if (((bitField & 33554432) !== 0)) {
  162. Promise._async.invoke(
  163. this._promiseFulfilled, this, maybePromise._value()
  164. );
  165. } else if (((bitField & 16777216) !== 0)) {
  166. Promise._async.invoke(
  167. this._promiseRejected, this, maybePromise._reason()
  168. );
  169. } else {
  170. this._promiseCancelled();
  171. }
  172. }
  173. };
  174. Promise.coroutine = function (generatorFunction, options) {
  175. if (typeof generatorFunction !== "function") {
  176. throw new TypeError("generatorFunction must be a function\u000a\u000a See http://goo.gl/MqrFmX\u000a");
  177. }
  178. var yieldHandler = Object(options).yieldHandler;
  179. var PromiseSpawn$ = PromiseSpawn;
  180. var stack = new Error().stack;
  181. return function () {
  182. var generator = generatorFunction.apply(this, arguments);
  183. var spawn = new PromiseSpawn$(undefined, undefined, yieldHandler,
  184. stack);
  185. var ret = spawn.promise();
  186. spawn._generator = generator;
  187. spawn._promiseFulfilled(undefined);
  188. return ret;
  189. };
  190. };
  191. Promise.coroutine.addYieldHandler = function(fn) {
  192. if (typeof fn !== "function") {
  193. throw new TypeError("expecting a function but got " + util.classString(fn));
  194. }
  195. yieldHandlers.push(fn);
  196. };
  197. Promise.spawn = function (generatorFunction) {
  198. debug.deprecated("Promise.spawn()", "Promise.coroutine()");
  199. if (typeof generatorFunction !== "function") {
  200. return apiRejection("generatorFunction must be a function\u000a\u000a See http://goo.gl/MqrFmX\u000a");
  201. }
  202. var spawn = new PromiseSpawn(generatorFunction, this);
  203. var ret = spawn.promise();
  204. spawn._run(Promise.spawn);
  205. return ret;
  206. };
  207. };