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

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: Fixed it 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 | « src/heap/heap.h ('k') | test/cctest/test-api.cc » ('j') | no next file with comments »
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
11 // ------------------------------------------------------------------- 11 // -------------------------------------------------------------------
12 // Imports 12 // Imports
13 13
14 var InternalArray = utils.InternalArray; 14 var InternalArray = utils.InternalArray;
15 var MakeTypeError; 15 var MakeTypeError;
16 var promiseCombinedDeferredSymbol = 16 var promiseCombinedDeferredSymbol =
17 utils.ImportNow("promise_combined_deferred_symbol"); 17 utils.ImportNow("promise_combined_deferred_symbol");
18 var promiseHasHandlerSymbol = 18 var promiseHasHandlerSymbol =
19 utils.ImportNow("promise_has_handler_symbol"); 19 utils.ImportNow("promise_has_handler_symbol");
20 var promiseOnRejectSymbol = utils.ImportNow("promise_on_reject_symbol"); 20 var promiseOnRejectSymbol = utils.ImportNow("promise_on_reject_symbol");
21 var promiseOnResolveSymbol = 21 var promiseOnResolveSymbol =
22 utils.ImportNow("promise_on_resolve_symbol"); 22 utils.ImportNow("promise_on_resolve_symbol");
23 var alreadyResolvedSymbol = utils.ImportNow("promise_already_resolved_symbol");
23 var promiseRawSymbol = utils.ImportNow("promise_raw_symbol"); 24 var promiseRawSymbol = utils.ImportNow("promise_raw_symbol");
24 var promiseStatusSymbol = utils.ImportNow("promise_status_symbol"); 25 var promiseStatusSymbol = utils.ImportNow("promise_status_symbol");
25 var promiseValueSymbol = utils.ImportNow("promise_value_symbol"); 26 var promiseValueSymbol = utils.ImportNow("promise_value_symbol");
26 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol"); 27 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
27 28
28 utils.Import(function(from) { 29 utils.Import(function(from) {
29 MakeTypeError = from.MakeTypeError; 30 MakeTypeError = from.MakeTypeError;
30 }); 31 });
31 32
32 // ------------------------------------------------------------------- 33 // -------------------------------------------------------------------
33 34
34 // Status values: 0 = pending, +1 = resolved, -1 = rejected 35 // Status values: 0 = pending, +1 = resolved, -1 = rejected
35 var lastMicrotaskId = 0; 36 var lastMicrotaskId = 0;
36 37
38 function CreateResolvingFunctions(promise) {
caitp (gmail) 2015/11/09 21:10:24 http://tc39.github.io/ecma262/#sec-createresolving
39 var alreadyResolved = { __proto__: null, value: false };
40
41 var resolve = function(value) {
caitp (gmail) 2015/11/09 21:10:24 I think the spec mandates that these functions don
42 var alreadyResolved = GET_PRIVATE(resolve, alreadyResolvedSymbol);
43 if (alreadyResolved.value === true) return;
44 alreadyResolved.value = true;
45 var promise = GET_PRIVATE(resolve, promiseValueSymbol);
46 if (value === promise) {
47 return PromiseReject(promise, MakeTypeError(kPromiseCyclic, value));
48 }
49 PromiseResolve(promise, value);
50 };
51
52 var reject = function(reason) {
53 if (alreadyResolved.value === true) return;
54 alreadyResolved.value = true;
55 PromiseReject(GET_PRIVATE(reject, promiseValueSymbol), reason);
56 };
57
58 SET_PRIVATE(resolve, promiseValueSymbol, promise);
59 SET_PRIVATE(resolve, alreadyResolvedSymbol, alreadyResolved);
60 SET_PRIVATE(reject, promiseValueSymbol, promise);
61 SET_PRIVATE(reject, alreadyResolvedSymbol, alreadyResolved);
62 return {
63 resolve: resolve,
64 reject: reject
65 };
66 }
67
68
37 var GlobalPromise = function Promise(resolver) { 69 var GlobalPromise = function Promise(resolver) {
38 if (resolver === promiseRawSymbol) return; 70 if (resolver === promiseRawSymbol) return;
39 if (!%_IsConstructCall()) throw MakeTypeError(kNotAPromise, this); 71 if (!%_IsConstructCall()) throw MakeTypeError(kNotAPromise, this);
40 if (!IS_CALLABLE(resolver)) 72 if (!IS_CALLABLE(resolver))
41 throw MakeTypeError(kResolverNotAFunction, resolver); 73 throw MakeTypeError(kResolverNotAFunction, resolver);
42 var promise = PromiseInit(this); 74 var promise = PromiseInit(this);
43 try { 75 try {
44 %DebugPushPromise(promise, Promise, resolver); 76 %DebugPushPromise(promise, Promise, resolver);
45 resolver(function(x) { PromiseResolve(promise, x) }, 77 var callbacks = CreateResolvingFunctions(promise);
46 function(r) { PromiseReject(promise, r) }); 78 resolver(callbacks.resolve, callbacks.reject);
47 } catch (e) { 79 } catch (e) {
48 PromiseReject(promise, e); 80 PromiseReject(promise, e);
49 } finally { 81 } finally {
50 %DebugPopPromise(); 82 %DebugPopPromise();
51 } 83 }
52 } 84 }
53 85
54 // Core functionality. 86 // Core functionality.
55 87
56 function PromiseSet(promise, status, value, onResolve, onReject) { 88 function PromiseSet(promise, status, value, onResolve, onReject) {
(...skipping 29 matching lines...) Expand all
86 118
87 function PromiseCoerce(constructor, x) { 119 function PromiseCoerce(constructor, x) {
88 if (!IsPromise(x) && IS_SPEC_OBJECT(x)) { 120 if (!IsPromise(x) && IS_SPEC_OBJECT(x)) {
89 var then; 121 var then;
90 try { 122 try {
91 then = x.then; 123 then = x.then;
92 } catch(r) { 124 } catch(r) {
93 return %_Call(PromiseRejected, constructor, r); 125 return %_Call(PromiseRejected, constructor, r);
94 } 126 }
95 if (IS_CALLABLE(then)) { 127 if (IS_CALLABLE(then)) {
96 var deferred = %_Call(PromiseDeferred, constructor); 128 var deferred = NewPromiseCapability(constructor);
97 try { 129 try {
98 %_Call(then, x, deferred.resolve, deferred.reject); 130 %_Call(then, x, deferred.resolve, deferred.reject);
99 } catch(r) { 131 } catch(r) {
100 deferred.reject(r); 132 deferred.reject(r);
101 } 133 }
102 return deferred.promise; 134 return deferred.promise;
103 } 135 }
104 } 136 }
105 return x; 137 return x;
106 } 138 }
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
154 186
155 function IsPromise(x) { 187 function IsPromise(x) {
156 return IS_SPEC_OBJECT(x) && HAS_DEFINED_PRIVATE(x, promiseStatusSymbol); 188 return IS_SPEC_OBJECT(x) && HAS_DEFINED_PRIVATE(x, promiseStatusSymbol);
157 } 189 }
158 190
159 function PromiseCreate() { 191 function PromiseCreate() {
160 return new GlobalPromise(PromiseNopResolver) 192 return new GlobalPromise(PromiseNopResolver)
161 } 193 }
162 194
163 function PromiseResolve(promise, x) { 195 function PromiseResolve(promise, x) {
164 PromiseDone(promise, +1, x, promiseOnResolveSymbol) 196 if (GET_PRIVATE(promise, promiseStatusSymbol) === 0) {
197 if (IS_SPEC_OBJECT(x)) {
198 // 25.4.1.3.2 steps 8-12
199 try {
200 var then = x.then;
201 } catch (e) {
202 return PromiseReject(promise, e);
203 }
204 if (IS_CALLABLE(then)) {
205 // PromiseResolveThenableJob
206 return %EnqueueMicrotask(function() {
207 try {
208 var callbacks = CreateResolvingFunctions(promise);
209 %_Call(then, x, callbacks.resolve, callbacks.reject);
210 } catch (e) {
211 PromiseReject(promise, e);
212 }
213 });
214 }
215 }
216 PromiseDone(promise, +1, x, promiseOnResolveSymbol);
217 }
165 } 218 }
166 219
167 function PromiseReject(promise, r) { 220 function PromiseReject(promise, r) {
168 // Check promise status to confirm that this reject has an effect. 221 // Check promise status to confirm that this reject has an effect.
169 // Call runtime for callbacks to the debugger or for unhandled reject. 222 // Call runtime for callbacks to the debugger or for unhandled reject.
170 if (GET_PRIVATE(promise, promiseStatusSymbol) == 0) { 223 if (GET_PRIVATE(promise, promiseStatusSymbol) == 0) {
171 var debug_is_active = DEBUG_IS_ACTIVE; 224 var debug_is_active = DEBUG_IS_ACTIVE;
172 if (debug_is_active || 225 if (debug_is_active ||
173 !HAS_DEFINED_PRIVATE(promise, promiseHasHandlerSymbol)) { 226 !HAS_DEFINED_PRIVATE(promise, promiseHasHandlerSymbol)) {
174 %PromiseRejectEvent(promise, r, debug_is_active); 227 %PromiseRejectEvent(promise, r, debug_is_active);
175 } 228 }
176 } 229 }
177 PromiseDone(promise, -1, r, promiseOnRejectSymbol) 230 PromiseDone(promise, -1, r, promiseOnRejectSymbol)
178 } 231 }
179 232
180 // Convenience. 233 // Convenience.
181 234
182 function PromiseDeferred() { 235 function NewPromiseCapability(C) {
caitp (gmail) 2015/11/09 21:10:24 Sort-of (but not quite) matches the spec concept "
183 if (this === GlobalPromise) { 236 if (C === GlobalPromise) {
184 // Optimized case, avoid extra closure. 237 // Optimized case, avoid extra closure.
185 var promise = PromiseInit(new GlobalPromise(promiseRawSymbol)); 238 var promise = PromiseInit(new GlobalPromise(promiseRawSymbol));
239 var callbacks = CreateResolvingFunctions(promise);
186 return { 240 return {
187 promise: promise, 241 promise: promise,
188 resolve: function(x) { PromiseResolve(promise, x) }, 242 resolve: callbacks.resolve,
189 reject: function(r) { PromiseReject(promise, r) } 243 reject: callbacks.reject
190 }; 244 };
191 } else { 245 } else {
192 var result = {promise: UNDEFINED, reject: UNDEFINED, resolve: UNDEFINED}; 246 var result = {promise: UNDEFINED, resolve: UNDEFINED, reject: UNDEFINED };
193 result.promise = new this(function(resolve, reject) { 247 result.promise = new C(function(resolve, reject) {
194 result.resolve = resolve; 248 result.resolve = resolve;
195 result.reject = reject; 249 result.reject = reject;
196 }); 250 });
197 return result; 251 return result;
198 } 252 }
199 } 253 }
200 254
255 function PromiseDeferred() {
256 return NewPromiseCapability(this);
257 }
258
201 function PromiseResolved(x) { 259 function PromiseResolved(x) {
202 if (this === GlobalPromise) { 260 if (this === GlobalPromise) {
203 // Optimized case, avoid extra closure. 261 // Optimized case, avoid extra closure.
204 return PromiseCreateAndSet(+1, x); 262 return PromiseCreateAndSet(+1, x);
205 } else { 263 } else {
206 return new this(function(resolve, reject) { resolve(x) }); 264 return new this(function(resolve, reject) { resolve(x) });
207 } 265 }
208 } 266 }
209 267
210 function PromiseRejected(r) { 268 function PromiseRejected(r) {
211 var promise; 269 var promise;
212 if (this === GlobalPromise) { 270 if (this === GlobalPromise) {
213 // Optimized case, avoid extra closure. 271 // Optimized case, avoid extra closure.
214 promise = PromiseCreateAndSet(-1, r); 272 promise = PromiseCreateAndSet(-1, r);
215 // The debug event for this would always be an uncaught promise reject, 273 // The debug event for this would always be an uncaught promise reject,
216 // which is usually simply noise. Do not trigger that debug event. 274 // which is usually simply noise. Do not trigger that debug event.
217 %PromiseRejectEvent(promise, r, false); 275 %PromiseRejectEvent(promise, r, false);
218 } else { 276 } else {
219 promise = new this(function(resolve, reject) { reject(r) }); 277 promise = new this(function(resolve, reject) { reject(r) });
220 } 278 }
221 return promise; 279 return promise;
222 } 280 }
223 281
224 // Simple chaining. 282 // Simple chaining.
225 283
226 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap 284 // PromiseChain a.k.a. flatMap
285 function PromiseChainInternal(constructor, onResolve, onReject) {
227 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; 286 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
228 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; 287 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
229 var deferred = %_Call(PromiseDeferred, this.constructor); 288 var deferred = NewPromiseCapability(constructor);
230 switch (GET_PRIVATE(this, promiseStatusSymbol)) { 289 switch (GET_PRIVATE(this, promiseStatusSymbol)) {
231 case UNDEFINED: 290 case UNDEFINED:
232 throw MakeTypeError(kNotAPromise, this); 291 throw MakeTypeError(kNotAPromise, this);
233 case 0: // Pending 292 case 0: // Pending
234 GET_PRIVATE(this, promiseOnResolveSymbol).push(onResolve, deferred); 293 GET_PRIVATE(this, promiseOnResolveSymbol).push(onResolve, deferred);
235 GET_PRIVATE(this, promiseOnRejectSymbol).push(onReject, deferred); 294 GET_PRIVATE(this, promiseOnRejectSymbol).push(onReject, deferred);
236 break; 295 break;
237 case +1: // Resolved 296 case +1: // Resolved
238 PromiseEnqueue(GET_PRIVATE(this, promiseValueSymbol), 297 PromiseEnqueue(GET_PRIVATE(this, promiseValueSymbol),
239 [onResolve, deferred], 298 [onResolve, deferred],
(...skipping 11 matching lines...) Expand all
251 break; 310 break;
252 } 311 }
253 // Mark this promise as having handler. 312 // Mark this promise as having handler.
254 SET_PRIVATE(this, promiseHasHandlerSymbol, true); 313 SET_PRIVATE(this, promiseHasHandlerSymbol, true);
255 if (DEBUG_IS_ACTIVE) { 314 if (DEBUG_IS_ACTIVE) {
256 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); 315 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this });
257 } 316 }
258 return deferred.promise; 317 return deferred.promise;
259 } 318 }
260 319
320 function PromiseChain(onResolve, onReject) {
321 return %_Call(PromiseChainInternal, this, this.constructor,
caitp (gmail) 2015/11/09 21:10:24 I think the goal is to get rid of this, but it's b
322 onResolve, onReject);
323 }
324
261 function PromiseCatch(onReject) { 325 function PromiseCatch(onReject) {
262 return this.then(UNDEFINED, onReject); 326 return this.then(UNDEFINED, onReject);
263 } 327 }
264 328
265 // Multi-unwrapped chaining with thenable coercion. 329 // Multi-unwrapped chaining with thenable coercion.
266 330
267 function PromiseThen(onResolve, onReject) { 331 function PromiseThen(onResolve, onReject) {
268 onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler; 332 onResolve = IS_CALLABLE(onResolve) ? onResolve : PromiseIdResolveHandler;
269 onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler; 333 onReject = IS_CALLABLE(onReject) ? onReject : PromiseIdRejectHandler;
270 var that = this; 334 var that = this;
271 var constructor = this.constructor; 335 var constructor = this.constructor;
272 return %_Call( 336 return %_Call(
273 PromiseChain, 337 PromiseChainInternal,
274 this, 338 this,
339 constructor,
275 function(x) { 340 function(x) {
276 x = PromiseCoerce(constructor, x); 341 x = PromiseCoerce(constructor, x);
277 if (x === that) { 342 if (x === that) {
278 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject); 343 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject);
279 return onReject(MakeTypeError(kPromiseCyclic, x)); 344 return onReject(MakeTypeError(kPromiseCyclic, x));
280 } else if (IsPromise(x)) { 345 } else if (IsPromise(x)) {
281 return x.then(onResolve, onReject); 346 return x.then(onResolve, onReject);
282 } else { 347 } else {
283 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve); 348 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve);
284 return onResolve(x); 349 return onResolve(x);
285 } 350 }
286 }, 351 },
287 onReject 352 onReject
288 ); 353 );
289 } 354 }
290 355
291 // Combinators. 356 // Combinators.
292 357
293 function PromiseCast(x) { 358 function PromiseCast(x) {
294 if (IsPromise(x) && x.constructor === this) { 359 if (IsPromise(x) && x.constructor === this) {
295 return x; 360 return x;
296 } else { 361 } else {
297 return new this(function(resolve) { resolve(x) }); 362 return new this(function(resolve) { resolve(x) });
298 } 363 }
299 } 364 }
300 365
301 function PromiseAll(iterable) { 366 function PromiseAll(iterable) {
302 var deferred = %_Call(PromiseDeferred, this); 367 var deferred = NewPromiseCapability(this);
303 var resolutions = []; 368 var resolutions = [];
304 try { 369 try {
305 var count = 0; 370 var count = 0;
306 var i = 0; 371 var i = 0;
307 for (var value of iterable) { 372 for (var value of iterable) {
308 var reject = function(r) { deferred.reject(r) }; 373 var reject = function(r) { deferred.reject(r) };
309 this.resolve(value).then( 374 this.resolve(value).then(
310 // Nested scope to get closure over current i. 375 // Nested scope to get closure over current i.
311 // TODO(arv): Use an inner let binding once available. 376 // TODO(arv): Use an inner let binding once available.
312 (function(i) { 377 (function(i) {
(...skipping 11 matching lines...) Expand all
324 deferred.resolve(resolutions); 389 deferred.resolve(resolutions);
325 } 390 }
326 391
327 } catch (e) { 392 } catch (e) {
328 deferred.reject(e) 393 deferred.reject(e)
329 } 394 }
330 return deferred.promise; 395 return deferred.promise;
331 } 396 }
332 397
333 function PromiseRace(iterable) { 398 function PromiseRace(iterable) {
334 var deferred = %_Call(PromiseDeferred, this); 399 var deferred = NewPromiseCapability(this);
335 try { 400 try {
336 for (var value of iterable) { 401 for (var value of iterable) {
337 var reject = function(r) { deferred.reject(r) }; 402 var reject = function(r) { deferred.reject(r) };
338 this.resolve(value).then(function(x) { deferred.resolve(x) }, reject); 403 this.resolve(value).then(function(x) { deferred.resolve(x) }, reject);
339 SET_PRIVATE(reject, promiseCombinedDeferredSymbol, deferred); 404 SET_PRIVATE(reject, promiseCombinedDeferredSymbol, deferred);
340 } 405 }
341 } catch (e) { 406 } catch (e) {
342 deferred.reject(e) 407 deferred.reject(e)
343 } 408 }
344 return deferred.promise; 409 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 473 // This allows extras to create promises quickly without building extra
409 // resolve/reject closures, and allows them to later resolve and reject any 474 // resolve/reject closures, and allows them to later resolve and reject any
410 // promise without having to hold on to those closures forever. 475 // promise without having to hold on to those closures forever.
411 utils.InstallFunctions(extrasUtils, 0, [ 476 utils.InstallFunctions(extrasUtils, 0, [
412 "createPromise", PromiseCreate, 477 "createPromise", PromiseCreate,
413 "resolvePromise", PromiseResolve, 478 "resolvePromise", PromiseResolve,
414 "rejectPromise", PromiseReject 479 "rejectPromise", PromiseReject
415 ]); 480 ]);
416 481
417 }) 482 })
OLDNEW
« no previous file with comments | « src/heap/heap.h ('k') | test/cctest/test-api.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698