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