Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(403)

Side by Side Diff: src/js/promise.js

Issue 1394463003: [es6] refactor Promise resolution (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: add bug reference to the TODOs Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | test/cctest/test-api.cc » ('j') | test/cctest/test-api.cc » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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 })
OLDNEW
« no previous file with comments | « no previous file | test/cctest/test-api.cc » ('j') | test/cctest/test-api.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698