OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 (function(global, utils, extrasUtils) { | 5 (function(global, utils, extrasUtils) { |
6 | 6 |
7 "use strict"; | 7 "use strict"; |
8 | 8 |
9 %CheckIsBootstrapping(); | 9 %CheckIsBootstrapping(); |
10 | 10 |
(...skipping 16 matching lines...) Expand all Loading... |
27 | 27 |
28 utils.Import(function(from) { | 28 utils.Import(function(from) { |
29 MakeTypeError = from.MakeTypeError; | 29 MakeTypeError = from.MakeTypeError; |
30 }); | 30 }); |
31 | 31 |
32 // ------------------------------------------------------------------- | 32 // ------------------------------------------------------------------- |
33 | 33 |
34 // Status values: 0 = pending, +1 = resolved, -1 = rejected | 34 // Status values: 0 = pending, +1 = resolved, -1 = rejected |
35 var lastMicrotaskId = 0; | 35 var lastMicrotaskId = 0; |
36 | 36 |
| 37 function CreateResolvingFunctions(promise) { |
| 38 var alreadyResolved = false; |
| 39 |
| 40 var resolve = function(value) { |
| 41 if (alreadyResolved === true) return; |
| 42 alreadyResolved = true; |
| 43 if (value === promise) { |
| 44 return PromiseReject(promise, MakeTypeError(kPromiseCyclic, value)); |
| 45 } |
| 46 PromiseResolve(promise, value); |
| 47 }; |
| 48 |
| 49 var reject = function(reason) { |
| 50 if (alreadyResolved === true) return; |
| 51 alreadyResolved = true; |
| 52 PromiseReject(promise, reason); |
| 53 }; |
| 54 |
| 55 return { |
| 56 __proto__: null, |
| 57 resolve: resolve, |
| 58 reject: reject |
| 59 }; |
| 60 } |
| 61 |
| 62 |
37 var GlobalPromise = function Promise(resolver) { | 63 var GlobalPromise = function Promise(resolver) { |
38 if (resolver === promiseRawSymbol) return; | 64 if (resolver === promiseRawSymbol) return; |
39 if (!%_IsConstructCall()) throw MakeTypeError(kNotAPromise, this); | 65 if (!%_IsConstructCall()) throw MakeTypeError(kNotAPromise, this); |
40 if (!IS_CALLABLE(resolver)) | 66 if (!IS_CALLABLE(resolver)) |
41 throw MakeTypeError(kResolverNotAFunction, resolver); | 67 throw MakeTypeError(kResolverNotAFunction, resolver); |
42 var promise = PromiseInit(this); | 68 var promise = PromiseInit(this); |
43 try { | 69 try { |
44 %DebugPushPromise(promise, Promise, resolver); | 70 %DebugPushPromise(promise, Promise, resolver); |
45 resolver(function(x) { PromiseResolve(promise, x) }, | 71 var callbacks = CreateResolvingFunctions(promise); |
46 function(r) { PromiseReject(promise, r) }); | 72 resolver(callbacks.resolve, callbacks.reject); |
47 } catch (e) { | 73 } catch (e) { |
48 PromiseReject(promise, e); | 74 PromiseReject(promise, e); |
49 } finally { | 75 } finally { |
50 %DebugPopPromise(); | 76 %DebugPopPromise(); |
51 } | 77 } |
52 } | 78 } |
53 | 79 |
54 // Core functionality. | 80 // Core functionality. |
55 | 81 |
56 function PromiseSet(promise, status, value, onResolve, onReject) { | 82 function PromiseSet(promise, status, value, onResolve, onReject) { |
(...skipping 29 matching lines...) Expand all Loading... |
86 | 112 |
87 function PromiseCoerce(constructor, x) { | 113 function PromiseCoerce(constructor, x) { |
88 if (!IsPromise(x) && IS_SPEC_OBJECT(x)) { | 114 if (!IsPromise(x) && IS_SPEC_OBJECT(x)) { |
89 var then; | 115 var then; |
90 try { | 116 try { |
91 then = x.then; | 117 then = x.then; |
92 } catch(r) { | 118 } catch(r) { |
93 return %_Call(PromiseRejected, constructor, r); | 119 return %_Call(PromiseRejected, constructor, r); |
94 } | 120 } |
95 if (IS_CALLABLE(then)) { | 121 if (IS_CALLABLE(then)) { |
96 var deferred = %_Call(PromiseDeferred, constructor); | 122 var deferred = NewPromiseCapability(constructor); |
97 try { | 123 try { |
98 %_Call(then, x, deferred.resolve, deferred.reject); | 124 %_Call(then, x, deferred.resolve, deferred.reject); |
99 } catch(r) { | 125 } catch(r) { |
100 deferred.reject(r); | 126 deferred.reject(r); |
101 } | 127 } |
102 return deferred.promise; | 128 return deferred.promise; |
103 } | 129 } |
104 } | 130 } |
105 return x; | 131 return x; |
106 } | 132 } |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
154 | 180 |
155 function IsPromise(x) { | 181 function IsPromise(x) { |
156 return IS_SPEC_OBJECT(x) && HAS_DEFINED_PRIVATE(x, promiseStatusSymbol); | 182 return IS_SPEC_OBJECT(x) && HAS_DEFINED_PRIVATE(x, promiseStatusSymbol); |
157 } | 183 } |
158 | 184 |
159 function PromiseCreate() { | 185 function PromiseCreate() { |
160 return new GlobalPromise(PromiseNopResolver) | 186 return new GlobalPromise(PromiseNopResolver) |
161 } | 187 } |
162 | 188 |
163 function PromiseResolve(promise, x) { | 189 function PromiseResolve(promise, x) { |
164 PromiseDone(promise, +1, x, promiseOnResolveSymbol) | 190 if (GET_PRIVATE(promise, promiseStatusSymbol) === 0) { |
| 191 if (IS_SPEC_OBJECT(x)) { |
| 192 // 25.4.1.3.2 steps 8-12 |
| 193 try { |
| 194 var then = x.then; |
| 195 } catch (e) { |
| 196 return PromiseReject(promise, e); |
| 197 } |
| 198 if (IS_CALLABLE(then)) { |
| 199 // PromiseResolveThenableJob |
| 200 return %EnqueueMicrotask(function() { |
| 201 try { |
| 202 var callbacks = CreateResolvingFunctions(promise); |
| 203 %_Call(then, x, callbacks.resolve, callbacks.reject); |
| 204 } catch (e) { |
| 205 PromiseReject(promise, e); |
| 206 } |
| 207 }); |
| 208 } |
| 209 } |
| 210 PromiseDone(promise, +1, x, promiseOnResolveSymbol); |
| 211 } |
165 } | 212 } |
166 | 213 |
167 function PromiseReject(promise, r) { | 214 function PromiseReject(promise, r) { |
168 // Check promise status to confirm that this reject has an effect. | 215 // Check promise status to confirm that this reject has an effect. |
169 // Call runtime for callbacks to the debugger or for unhandled reject. | 216 // Call runtime for callbacks to the debugger or for unhandled reject. |
170 if (GET_PRIVATE(promise, promiseStatusSymbol) == 0) { | 217 if (GET_PRIVATE(promise, promiseStatusSymbol) == 0) { |
171 var debug_is_active = DEBUG_IS_ACTIVE; | 218 var debug_is_active = DEBUG_IS_ACTIVE; |
172 if (debug_is_active || | 219 if (debug_is_active || |
173 !HAS_DEFINED_PRIVATE(promise, promiseHasHandlerSymbol)) { | 220 !HAS_DEFINED_PRIVATE(promise, promiseHasHandlerSymbol)) { |
174 %PromiseRejectEvent(promise, r, debug_is_active); | 221 %PromiseRejectEvent(promise, r, debug_is_active); |
175 } | 222 } |
176 } | 223 } |
177 PromiseDone(promise, -1, r, promiseOnRejectSymbol) | 224 PromiseDone(promise, -1, r, promiseOnRejectSymbol) |
178 } | 225 } |
179 | 226 |
180 // Convenience. | 227 // Convenience. |
181 | 228 |
182 function PromiseDeferred() { | 229 function NewPromiseCapability(C) { |
183 if (this === GlobalPromise) { | 230 if (C === GlobalPromise) { |
184 // Optimized case, avoid extra closure. | 231 // Optimized case, avoid extra closure. |
185 var promise = PromiseInit(new GlobalPromise(promiseRawSymbol)); | 232 var promise = PromiseInit(new GlobalPromise(promiseRawSymbol)); |
| 233 var callbacks = CreateResolvingFunctions(promise); |
186 return { | 234 return { |
187 promise: promise, | 235 promise: promise, |
188 resolve: function(x) { PromiseResolve(promise, x) }, | 236 resolve: callbacks.resolve, |
189 reject: function(r) { PromiseReject(promise, r) } | 237 reject: callbacks.reject |
190 }; | 238 }; |
191 } else { | 239 } else { |
192 var result = {promise: UNDEFINED, reject: UNDEFINED, resolve: UNDEFINED}; | 240 var result = {promise: UNDEFINED, resolve: UNDEFINED, reject: UNDEFINED }; |
193 result.promise = new this(function(resolve, reject) { | 241 result.promise = new C(function(resolve, reject) { |
194 result.resolve = resolve; | 242 result.resolve = resolve; |
195 result.reject = reject; | 243 result.reject = reject; |
196 }); | 244 }); |
197 return result; | 245 return result; |
198 } | 246 } |
199 } | 247 } |
200 | 248 |
| 249 function PromiseDeferred() { |
| 250 return NewPromiseCapability(this); |
| 251 } |
| 252 |
201 function PromiseResolved(x) { | 253 function PromiseResolved(x) { |
202 if (this === GlobalPromise) { | 254 if (this === GlobalPromise) { |
203 // Optimized case, avoid extra closure. | 255 // Optimized case, avoid extra closure. |
204 return PromiseCreateAndSet(+1, x); | 256 return PromiseCreateAndSet(+1, x); |
205 } else { | 257 } else { |
206 return new this(function(resolve, reject) { resolve(x) }); | 258 return new this(function(resolve, reject) { resolve(x) }); |
207 } | 259 } |
208 } | 260 } |
209 | 261 |
210 function PromiseRejected(r) { | 262 function PromiseRejected(r) { |
211 var promise; | 263 var promise; |
212 if (this === GlobalPromise) { | 264 if (this === GlobalPromise) { |
213 // Optimized case, avoid extra closure. | 265 // Optimized case, avoid extra closure. |
214 promise = PromiseCreateAndSet(-1, r); | 266 promise = PromiseCreateAndSet(-1, r); |
215 // The debug event for this would always be an uncaught promise reject, | 267 // The debug event for this would always be an uncaught promise reject, |
216 // which is usually simply noise. Do not trigger that debug event. | 268 // which is usually simply noise. Do not trigger that debug event. |
217 %PromiseRejectEvent(promise, r, false); | 269 %PromiseRejectEvent(promise, r, false); |
218 } else { | 270 } else { |
219 promise = new this(function(resolve, reject) { reject(r) }); | 271 promise = new this(function(resolve, reject) { reject(r) }); |
220 } | 272 } |
221 return promise; | 273 return promise; |
222 } | 274 } |
223 | 275 |
224 // Simple chaining. | 276 // Simple chaining. |
225 | 277 |
226 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap | 278 // PromiseChain a.k.a. flatMap |
| 279 function PromiseChainInternal(constructor, onResolve, onReject) { |
227 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; | 280 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; |
228 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; | 281 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; |
229 var deferred = %_Call(PromiseDeferred, this.constructor); | 282 var deferred = NewPromiseCapability(constructor); |
230 switch (GET_PRIVATE(this, promiseStatusSymbol)) { | 283 switch (GET_PRIVATE(this, promiseStatusSymbol)) { |
231 case UNDEFINED: | 284 case UNDEFINED: |
232 throw MakeTypeError(kNotAPromise, this); | 285 throw MakeTypeError(kNotAPromise, this); |
233 case 0: // Pending | 286 case 0: // Pending |
234 GET_PRIVATE(this, promiseOnResolveSymbol).push(onResolve, deferred); | 287 GET_PRIVATE(this, promiseOnResolveSymbol).push(onResolve, deferred); |
235 GET_PRIVATE(this, promiseOnRejectSymbol).push(onReject, deferred); | 288 GET_PRIVATE(this, promiseOnRejectSymbol).push(onReject, deferred); |
236 break; | 289 break; |
237 case +1: // Resolved | 290 case +1: // Resolved |
238 PromiseEnqueue(GET_PRIVATE(this, promiseValueSymbol), | 291 PromiseEnqueue(GET_PRIVATE(this, promiseValueSymbol), |
239 [onResolve, deferred], | 292 [onResolve, deferred], |
(...skipping 11 matching lines...) Expand all Loading... |
251 break; | 304 break; |
252 } | 305 } |
253 // Mark this promise as having handler. | 306 // Mark this promise as having handler. |
254 SET_PRIVATE(this, promiseHasHandlerSymbol, true); | 307 SET_PRIVATE(this, promiseHasHandlerSymbol, true); |
255 if (DEBUG_IS_ACTIVE) { | 308 if (DEBUG_IS_ACTIVE) { |
256 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); | 309 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); |
257 } | 310 } |
258 return deferred.promise; | 311 return deferred.promise; |
259 } | 312 } |
260 | 313 |
| 314 function PromiseChain(onResolve, onReject) { |
| 315 return %_Call(PromiseChainInternal, this, this.constructor, |
| 316 onResolve, onReject); |
| 317 } |
| 318 |
261 function PromiseCatch(onReject) { | 319 function PromiseCatch(onReject) { |
262 return this.then(UNDEFINED, onReject); | 320 return this.then(UNDEFINED, onReject); |
263 } | 321 } |
264 | 322 |
265 // Multi-unwrapped chaining with thenable coercion. | 323 // Multi-unwrapped chaining with thenable coercion. |
266 | 324 |
267 function PromiseThen(onResolve, onReject) { | 325 function PromiseThen(onResolve, onReject) { |
268 onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler; | 326 onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler; |
269 onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler; | 327 onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler; |
270 var that = this; | 328 var that = this; |
271 var constructor = this.constructor; | 329 var constructor = this.constructor; |
272 return %_Call( | 330 return %_Call( |
273 PromiseChain, | 331 PromiseChainInternal, |
274 this, | 332 this, |
| 333 constructor, |
275 function(x) { | 334 function(x) { |
276 x = PromiseCoerce(constructor, x); | 335 x = PromiseCoerce(constructor, x); |
277 if (x === that) { | 336 if (x === that) { |
278 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject); | 337 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject); |
279 return onReject(MakeTypeError(kPromiseCyclic, x)); | 338 return onReject(MakeTypeError(kPromiseCyclic, x)); |
280 } else if (IsPromise(x)) { | 339 } else if (IsPromise(x)) { |
281 return x.then(onResolve, onReject); | 340 return x.then(onResolve, onReject); |
282 } else { | 341 } else { |
283 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve); | 342 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve); |
284 return onResolve(x); | 343 return onResolve(x); |
285 } | 344 } |
286 }, | 345 }, |
287 onReject | 346 onReject |
288 ); | 347 ); |
289 } | 348 } |
290 | 349 |
291 // Combinators. | 350 // Combinators. |
292 | 351 |
293 function PromiseCast(x) { | 352 function PromiseCast(x) { |
294 if (IsPromise(x) && x.constructor === this) { | 353 if (IsPromise(x) && x.constructor === this) { |
295 return x; | 354 return x; |
296 } else { | 355 } else { |
297 return new this(function(resolve) { resolve(x) }); | 356 return new this(function(resolve) { resolve(x) }); |
298 } | 357 } |
299 } | 358 } |
300 | 359 |
301 function PromiseAll(iterable) { | 360 function PromiseAll(iterable) { |
302 var deferred = %_Call(PromiseDeferred, this); | 361 var deferred = NewPromiseCapability(this); |
303 var resolutions = []; | 362 var resolutions = []; |
304 try { | 363 try { |
305 var count = 0; | 364 var count = 0; |
306 var i = 0; | 365 var i = 0; |
307 for (var value of iterable) { | 366 for (var value of iterable) { |
308 var reject = function(r) { deferred.reject(r) }; | 367 var reject = function(r) { deferred.reject(r) }; |
309 this.resolve(value).then( | 368 this.resolve(value).then( |
310 // Nested scope to get closure over current i. | 369 // Nested scope to get closure over current i. |
311 // TODO(arv): Use an inner let binding once available. | 370 // TODO(arv): Use an inner let binding once available. |
312 (function(i) { | 371 (function(i) { |
(...skipping 11 matching lines...) Expand all Loading... |
324 deferred.resolve(resolutions); | 383 deferred.resolve(resolutions); |
325 } | 384 } |
326 | 385 |
327 } catch (e) { | 386 } catch (e) { |
328 deferred.reject(e) | 387 deferred.reject(e) |
329 } | 388 } |
330 return deferred.promise; | 389 return deferred.promise; |
331 } | 390 } |
332 | 391 |
333 function PromiseRace(iterable) { | 392 function PromiseRace(iterable) { |
334 var deferred = %_Call(PromiseDeferred, this); | 393 var deferred = NewPromiseCapability(this); |
335 try { | 394 try { |
336 for (var value of iterable) { | 395 for (var value of iterable) { |
337 var reject = function(r) { deferred.reject(r) }; | 396 var reject = function(r) { deferred.reject(r) }; |
338 this.resolve(value).then(function(x) { deferred.resolve(x) }, reject); | 397 this.resolve(value).then(function(x) { deferred.resolve(x) }, reject); |
339 SET_PRIVATE(reject, promiseCombinedDeferredSymbol, deferred); | 398 SET_PRIVATE(reject, promiseCombinedDeferredSymbol, deferred); |
340 } | 399 } |
341 } catch (e) { | 400 } catch (e) { |
342 deferred.reject(e) | 401 deferred.reject(e) |
343 } | 402 } |
344 return deferred.promise; | 403 return deferred.promise; |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
408 // This allows extras to create promises quickly without building extra | 467 // This allows extras to create promises quickly without building extra |
409 // resolve/reject closures, and allows them to later resolve and reject any | 468 // resolve/reject closures, and allows them to later resolve and reject any |
410 // promise without having to hold on to those closures forever. | 469 // promise without having to hold on to those closures forever. |
411 utils.InstallFunctions(extrasUtils, 0, [ | 470 utils.InstallFunctions(extrasUtils, 0, [ |
412 "createPromise", PromiseCreate, | 471 "createPromise", PromiseCreate, |
413 "resolvePromise", PromiseResolve, | 472 "resolvePromise", PromiseResolve, |
414 "rejectPromise", PromiseReject | 473 "rejectPromise", PromiseReject |
415 ]); | 474 ]); |
416 | 475 |
417 }) | 476 }) |
OLD | NEW |