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

Side by Side Diff: src/promise.js

Issue 1054763003: Wrap promise implementation in a function. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 5 years, 8 months 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/mirror-debugger.js ('k') | no next file » | 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 var $promiseCreate;
6 var $promiseResolve;
7 var $promiseReject;
8 var $promiseChain;
9 var $promiseCatch;
10 var $promiseThen;
11 var $promiseHasUserDefinedRejectHandler;
12 var $promiseStatus;
13 var $promiseValue;
14
15 (function() {
16
5 "use strict"; 17 "use strict";
6 18
7 // This file relies on the fact that the following declaration has been made 19 %CheckIsBootstrapping();
8 // in runtime.js:
9 // var $Object = global.Object
10 20
11 // For bootstrapper. 21 // -------------------------------------------------------------------
12
13 var IsPromise;
14 var PromiseCreate;
15 var PromiseResolve;
16 var PromiseReject;
17 var PromiseChain;
18 var PromiseCatch;
19 var PromiseThen;
20 var PromiseHasRejectHandler;
21 var PromiseHasUserDefinedRejectHandler;
22
23 // mirror-debugger.js currently uses builtins.promiseStatus. It would be nice
24 // if we could move these property names into the closure below.
25 // TODO(jkummerow/rossberg/yangguo): Find a better solution.
26 22
27 // Status values: 0 = pending, +1 = resolved, -1 = rejected 23 // Status values: 0 = pending, +1 = resolved, -1 = rejected
28 var promiseStatus = GLOBAL_PRIVATE("Promise#status"); 24 var promiseStatus = GLOBAL_PRIVATE("Promise#status");
29 var promiseValue = GLOBAL_PRIVATE("Promise#value"); 25 var promiseValue = GLOBAL_PRIVATE("Promise#value");
30 var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve"); 26 var promiseOnResolve = GLOBAL_PRIVATE("Promise#onResolve");
31 var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject"); 27 var promiseOnReject = GLOBAL_PRIVATE("Promise#onReject");
32 var promiseRaw = GLOBAL_PRIVATE("Promise#raw"); 28 var promiseRaw = GLOBAL_PRIVATE("Promise#raw");
33 var promiseHasHandler = %PromiseHasHandlerSymbol(); 29 var promiseHasHandler = %PromiseHasHandlerSymbol();
34 var lastMicrotaskId = 0; 30 var lastMicrotaskId = 0;
35 31
36 32 var GlobalPromise = function Promise(resolver) {
37 (function() { 33 if (resolver === promiseRaw) return;
38 34 if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]);
39 var $Promise = function Promise(resolver) { 35 if (!IS_SPEC_FUNCTION(resolver))
40 if (resolver === promiseRaw) return; 36 throw MakeTypeError('resolver_not_a_function', [resolver]);
41 if (!%_IsConstructCall()) throw MakeTypeError('not_a_promise', [this]); 37 var promise = PromiseInit(this);
42 if (!IS_SPEC_FUNCTION(resolver)) 38 try {
43 throw MakeTypeError('resolver_not_a_function', [resolver]); 39 %DebugPushPromise(promise, Promise);
44 var promise = PromiseInit(this); 40 resolver(function(x) { PromiseResolve(promise, x) },
41 function(r) { PromiseReject(promise, r) });
42 } catch (e) {
43 PromiseReject(promise, e);
44 } finally {
45 %DebugPopPromise();
46 }
47 }
48
49 // Core functionality.
50
51 function PromiseSet(promise, status, value, onResolve, onReject) {
52 SET_PRIVATE(promise, promiseStatus, status);
53 SET_PRIVATE(promise, promiseValue, value);
54 SET_PRIVATE(promise, promiseOnResolve, onResolve);
55 SET_PRIVATE(promise, promiseOnReject, onReject);
56 if (DEBUG_IS_ACTIVE) {
57 %DebugPromiseEvent({ promise: promise, status: status, value: value });
58 }
59 return promise;
60 }
61
62 function PromiseCreateAndSet(status, value) {
63 var promise = new GlobalPromise(promiseRaw);
64 // If debug is active, notify about the newly created promise first.
65 if (DEBUG_IS_ACTIVE) PromiseSet(promise, 0, UNDEFINED);
66 return PromiseSet(promise, status, value);
67 }
68
69 function PromiseInit(promise) {
70 return PromiseSet(
71 promise, 0, UNDEFINED, new InternalArray, new InternalArray)
72 }
73
74 function PromiseDone(promise, status, value, promiseQueue) {
75 if (GET_PRIVATE(promise, promiseStatus) === 0) {
76 var tasks = GET_PRIVATE(promise, promiseQueue);
77 if (tasks.length) PromiseEnqueue(value, tasks, status);
78 PromiseSet(promise, status, value);
79 }
80 }
81
82 function PromiseCoerce(constructor, x) {
83 if (!IsPromise(x) && IS_SPEC_OBJECT(x)) {
84 var then;
45 try { 85 try {
46 %DebugPushPromise(promise, Promise); 86 then = x.then;
47 resolver(function(x) { PromiseResolve(promise, x) }, 87 } catch(r) {
48 function(r) { PromiseReject(promise, r) }); 88 return %_CallFunction(constructor, r, PromiseRejected);
49 } catch (e) { 89 }
50 PromiseReject(promise, e); 90 if (IS_SPEC_FUNCTION(then)) {
51 } finally { 91 var deferred = %_CallFunction(constructor, PromiseDeferred);
52 %DebugPopPromise();
53 }
54 }
55
56 // Core functionality.
57
58 function PromiseSet(promise, status, value, onResolve, onReject) {
59 SET_PRIVATE(promise, promiseStatus, status);
60 SET_PRIVATE(promise, promiseValue, value);
61 SET_PRIVATE(promise, promiseOnResolve, onResolve);
62 SET_PRIVATE(promise, promiseOnReject, onReject);
63 if (DEBUG_IS_ACTIVE) {
64 %DebugPromiseEvent({ promise: promise, status: status, value: value });
65 }
66 return promise;
67 }
68
69 function PromiseCreateAndSet(status, value) {
70 var promise = new $Promise(promiseRaw);
71 // If debug is active, notify about the newly created promise first.
72 if (DEBUG_IS_ACTIVE) PromiseSet(promise, 0, UNDEFINED);
73 return PromiseSet(promise, status, value);
74 }
75
76 function PromiseInit(promise) {
77 return PromiseSet(
78 promise, 0, UNDEFINED, new InternalArray, new InternalArray)
79 }
80
81 function PromiseDone(promise, status, value, promiseQueue) {
82 if (GET_PRIVATE(promise, promiseStatus) === 0) {
83 var tasks = GET_PRIVATE(promise, promiseQueue);
84 if (tasks.length) PromiseEnqueue(value, tasks, status);
85 PromiseSet(promise, status, value);
86 }
87 }
88
89 function PromiseCoerce(constructor, x) {
90 if (!IsPromise(x) && IS_SPEC_OBJECT(x)) {
91 var then;
92 try { 92 try {
93 then = x.then; 93 %_CallFunction(x, deferred.resolve, deferred.reject, then);
94 } catch(r) { 94 } catch(r) {
95 return %_CallFunction(constructor, r, PromiseRejected); 95 deferred.reject(r);
96 } 96 }
97 if (IS_SPEC_FUNCTION(then)) { 97 return deferred.promise;
98 var deferred = %_CallFunction(constructor, PromiseDeferred); 98 }
99 try { 99 }
100 %_CallFunction(x, deferred.resolve, deferred.reject, then); 100 return x;
101 } catch(r) { 101 }
102 deferred.reject(r); 102
103 } 103 function PromiseHandle(value, handler, deferred) {
104 return deferred.promise; 104 try {
105 %DebugPushPromise(deferred.promise, PromiseHandle);
106 DEBUG_PREPARE_STEP_IN_IF_STEPPING(handler);
107 var result = handler(value);
108 if (result === deferred.promise)
109 throw MakeTypeError('promise_cyclic', [result]);
110 else if (IsPromise(result))
111 %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain);
112 else
113 deferred.resolve(result);
114 } catch (exception) {
115 try { deferred.reject(exception); } catch (e) { }
116 } finally {
117 %DebugPopPromise();
118 }
119 }
120
121 function PromiseEnqueue(value, tasks, status) {
122 var id, name, instrumenting = DEBUG_IS_ACTIVE;
123 %EnqueueMicrotask(function() {
124 if (instrumenting) {
125 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name });
126 }
127 for (var i = 0; i < tasks.length; i += 2) {
128 PromiseHandle(value, tasks[i], tasks[i + 1])
129 }
130 if (instrumenting) {
131 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name });
132 }
133 });
134 if (instrumenting) {
135 id = ++lastMicrotaskId;
136 name = status > 0 ? "Promise.resolve" : "Promise.reject";
137 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name });
138 }
139 }
140
141 function PromiseIdResolveHandler(x) { return x }
142 function PromiseIdRejectHandler(r) { throw r }
143
144 function PromiseNopResolver() {}
145
146 // -------------------------------------------------------------------
147 // Define exported functions.
148
149 // For bootstrapper.
150
151 function IsPromise(x) {
152 return IS_SPEC_OBJECT(x) && HAS_DEFINED_PRIVATE(x, promiseStatus);
153 }
154
155 function PromiseCreate() {
156 return new GlobalPromise(PromiseNopResolver)
157 }
158
159 function PromiseResolve(promise, x) {
160 PromiseDone(promise, +1, x, promiseOnResolve)
161 }
162
163 function PromiseReject(promise, r) {
164 // Check promise status to confirm that this reject has an effect.
165 // Call runtime for callbacks to the debugger or for unhandled reject.
166 if (GET_PRIVATE(promise, promiseStatus) == 0) {
167 var debug_is_active = DEBUG_IS_ACTIVE;
168 if (debug_is_active || !HAS_DEFINED_PRIVATE(promise, promiseHasHandler)) {
169 %PromiseRejectEvent(promise, r, debug_is_active);
170 }
171 }
172 PromiseDone(promise, -1, r, promiseOnReject)
173 }
174
175 // Convenience.
176
177 function PromiseDeferred() {
178 if (this === GlobalPromise) {
179 // Optimized case, avoid extra closure.
180 var promise = PromiseInit(new GlobalPromise(promiseRaw));
181 return {
182 promise: promise,
183 resolve: function(x) { PromiseResolve(promise, x) },
184 reject: function(r) { PromiseReject(promise, r) }
185 };
186 } else {
187 var result = {};
188 result.promise = new this(function(resolve, reject) {
189 result.resolve = resolve;
190 result.reject = reject;
191 })
192 return result;
193 }
194 }
195
196 function PromiseResolved(x) {
197 if (this === GlobalPromise) {
198 // Optimized case, avoid extra closure.
199 return PromiseCreateAndSet(+1, x);
200 } else {
201 return new this(function(resolve, reject) { resolve(x) });
202 }
203 }
204
205 function PromiseRejected(r) {
206 var promise;
207 if (this === GlobalPromise) {
208 // Optimized case, avoid extra closure.
209 promise = PromiseCreateAndSet(-1, r);
210 // The debug event for this would always be an uncaught promise reject,
211 // which is usually simply noise. Do not trigger that debug event.
212 %PromiseRejectEvent(promise, r, false);
213 } else {
214 promise = new this(function(resolve, reject) { reject(r) });
215 }
216 return promise;
217 }
218
219 // Simple chaining.
220
221 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap
222 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
223 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
224 var deferred = %_CallFunction(this.constructor, PromiseDeferred);
225 switch (GET_PRIVATE(this, promiseStatus)) {
226 case UNDEFINED:
227 throw MakeTypeError('not_a_promise', [this]);
228 case 0: // Pending
229 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred);
230 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
231 break;
232 case +1: // Resolved
233 PromiseEnqueue(GET_PRIVATE(this, promiseValue),
234 [onResolve, deferred],
235 +1);
236 break;
237 case -1: // Rejected
238 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) {
239 // Promise has already been rejected, but had no handler.
240 // Revoke previously triggered reject event.
241 %PromiseRevokeReject(this);
105 } 242 }
106 } 243 PromiseEnqueue(GET_PRIVATE(this, promiseValue),
107 return x; 244 [onReject, deferred],
108 } 245 -1);
109 246 break;
110 function PromiseHandle(value, handler, deferred) { 247 }
111 try { 248 // Mark this promise as having handler.
112 %DebugPushPromise(deferred.promise, PromiseHandle); 249 SET_PRIVATE(this, promiseHasHandler, true);
113 DEBUG_PREPARE_STEP_IN_IF_STEPPING(handler); 250 if (DEBUG_IS_ACTIVE) {
114 var result = handler(value); 251 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this });
115 if (result === deferred.promise) 252 }
116 throw MakeTypeError('promise_cyclic', [result]); 253 return deferred.promise;
117 else if (IsPromise(result)) 254 }
118 %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain); 255
119 else 256 function PromiseCatch(onReject) {
120 deferred.resolve(result); 257 return this.then(UNDEFINED, onReject);
121 } catch (exception) { 258 }
122 try { deferred.reject(exception); } catch (e) { } 259
123 } finally { 260 // Multi-unwrapped chaining with thenable coercion.
124 %DebugPopPromise(); 261
125 } 262 function PromiseThen(onResolve, onReject) {
126 } 263 onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve
127 264 : PromiseIdResolveHandler;
128 function PromiseEnqueue(value, tasks, status) { 265 onReject = IS_SPEC_FUNCTION(onReject) ? onReject
129 var id, name, instrumenting = DEBUG_IS_ACTIVE; 266 : PromiseIdRejectHandler;
130 %EnqueueMicrotask(function() { 267 var that = this;
131 if (instrumenting) { 268 var constructor = this.constructor;
132 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); 269 return %_CallFunction(
270 this,
271 function(x) {
272 x = PromiseCoerce(constructor, x);
273 if (x === that) {
274 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject);
275 return onReject(MakeTypeError('promise_cyclic', [x]));
276 } else if (IsPromise(x)) {
277 return x.then(onResolve, onReject);
278 } else {
279 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve);
280 return onResolve(x);
133 } 281 }
134 for (var i = 0; i < tasks.length; i += 2) { 282 },
135 PromiseHandle(value, tasks[i], tasks[i + 1]) 283 onReject,
136 } 284 PromiseChain
137 if (instrumenting) { 285 );
138 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); 286 }
139 } 287
140 }); 288 // Combinators.
141 if (instrumenting) { 289
142 id = ++lastMicrotaskId; 290 function PromiseCast(x) {
143 name = status > 0 ? "Promise.resolve" : "Promise.reject"; 291 // TODO(rossberg): cannot do better until we support @@create.
144 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); 292 return IsPromise(x) ? x : new this(function(resolve) { resolve(x) });
145 } 293 }
146 } 294
147 295 function PromiseAll(iterable) {
148 function PromiseIdResolveHandler(x) { return x } 296 var deferred = %_CallFunction(this, PromiseDeferred);
149 function PromiseIdRejectHandler(r) { throw r } 297 var resolutions = [];
150 298 try {
151 function PromiseNopResolver() {} 299 var count = 0;
152 300 var i = 0;
153 // ------------------------------------------------------------------- 301 for (var value of iterable) {
154 // Define exported functions. 302 this.resolve(value).then(
155 303 // Nested scope to get closure over current i.
156 // For bootstrapper. 304 // TODO(arv): Use an inner let binding once available.
157 305 (function(i) {
158 IsPromise = function IsPromise(x) { 306 return function(x) {
159 return IS_SPEC_OBJECT(x) && HAS_DEFINED_PRIVATE(x, promiseStatus); 307 resolutions[i] = x;
160 } 308 if (--count === 0) deferred.resolve(resolutions);
161 309 }
162 PromiseCreate = function PromiseCreate() { 310 })(i),
163 return new $Promise(PromiseNopResolver) 311 function(r) { deferred.reject(r); });
164 } 312 ++i;
165 313 ++count;
166 PromiseResolve = function PromiseResolve(promise, x) { 314 }
167 PromiseDone(promise, +1, x, promiseOnResolve) 315
168 } 316 if (count === 0) {
169 317 deferred.resolve(resolutions);
170 PromiseReject = function PromiseReject(promise, r) { 318 }
171 // Check promise status to confirm that this reject has an effect. 319
172 // Call runtime for callbacks to the debugger or for unhandled reject. 320 } catch (e) {
173 if (GET_PRIVATE(promise, promiseStatus) == 0) { 321 deferred.reject(e)
174 var debug_is_active = DEBUG_IS_ACTIVE; 322 }
175 if (debug_is_active || !HAS_DEFINED_PRIVATE(promise, promiseHasHandler)) { 323 return deferred.promise;
176 %PromiseRejectEvent(promise, r, debug_is_active); 324 }
177 } 325
178 } 326 function PromiseRace(iterable) {
179 PromiseDone(promise, -1, r, promiseOnReject) 327 var deferred = %_CallFunction(this, PromiseDeferred);
180 } 328 try {
181 329 for (var value of iterable) {
182 // Convenience. 330 this.resolve(value).then(
183 331 function(x) { deferred.resolve(x) },
184 function PromiseDeferred() { 332 function(r) { deferred.reject(r) });
185 if (this === $Promise) { 333 }
186 // Optimized case, avoid extra closure. 334 } catch (e) {
187 var promise = PromiseInit(new $Promise(promiseRaw)); 335 deferred.reject(e)
188 return { 336 }
189 promise: promise, 337 return deferred.promise;
190 resolve: function(x) { PromiseResolve(promise, x) }, 338 }
191 reject: function(r) { PromiseReject(promise, r) } 339
192 }; 340
193 } else { 341 // Utility for debugger
194 var result = {}; 342
195 result.promise = new this(function(resolve, reject) { 343 function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
196 result.resolve = resolve; 344 var queue = GET_PRIVATE(promise, promiseOnReject);
197 result.reject = reject; 345 if (IS_UNDEFINED(queue)) return false;
198 }) 346 for (var i = 0; i < queue.length; i += 2) {
199 return result; 347 if (queue[i] != PromiseIdRejectHandler) return true;
200 } 348 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) {
201 } 349 return true;
202 350 }
203 function PromiseResolved(x) { 351 }
204 if (this === $Promise) { 352 return false;
205 // Optimized case, avoid extra closure. 353 }
206 return PromiseCreateAndSet(+1, x); 354
207 } else { 355 // Return whether the promise will be handled by a user-defined reject
208 return new this(function(resolve, reject) { resolve(x) }); 356 // handler somewhere down the promise chain. For this, we do a depth-first
209 } 357 // search for a reject handler that's not the default PromiseIdRejectHandler.
210 } 358 function PromiseHasUserDefinedRejectHandler() {
211 359 return PromiseHasUserDefinedRejectHandlerRecursive(this);
212 function PromiseRejected(r) { 360 };
213 var promise; 361
214 if (this === $Promise) { 362 // -------------------------------------------------------------------
215 // Optimized case, avoid extra closure. 363 // Install exported functions.
216 promise = PromiseCreateAndSet(-1, r); 364
217 // The debug event for this would always be an uncaught promise reject, 365 %AddNamedProperty(global, 'Promise', GlobalPromise, DONT_ENUM);
218 // which is usually simply noise. Do not trigger that debug event. 366 %AddNamedProperty(GlobalPromise.prototype, symbolToStringTag, "Promise",
219 %PromiseRejectEvent(promise, r, false); 367 DONT_ENUM | READ_ONLY);
220 } else { 368
221 promise = new this(function(resolve, reject) { reject(r) }); 369 InstallFunctions(GlobalPromise, DONT_ENUM, [
222 } 370 "defer", PromiseDeferred,
223 return promise; 371 "accept", PromiseResolved,
224 } 372 "reject", PromiseRejected,
225 373 "all", PromiseAll,
226 // Simple chaining. 374 "race", PromiseRace,
227 375 "resolve", PromiseCast
228 PromiseChain = function PromiseChain(onResolve, onReject) { // a.k.a. 376 ]);
229 // flatMap 377
230 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; 378 InstallFunctions(GlobalPromise.prototype, DONT_ENUM, [
231 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; 379 "chain", PromiseChain,
232 var deferred = %_CallFunction(this.constructor, PromiseDeferred); 380 "then", PromiseThen,
233 switch (GET_PRIVATE(this, promiseStatus)) { 381 "catch", PromiseCatch
234 case UNDEFINED: 382 ]);
235 throw MakeTypeError('not_a_promise', [this]); 383
236 case 0: // Pending 384 $promiseCreate = PromiseCreate;
237 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred); 385 $promiseResolve = PromiseResolve;
238 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred); 386 $promiseReject = PromiseReject;
239 break; 387 $promiseChain = PromiseChain;
240 case +1: // Resolved 388 $promiseCatch = PromiseCatch;
241 PromiseEnqueue(GET_PRIVATE(this, promiseValue), 389 $promiseThen = PromiseThen;
242 [onResolve, deferred], 390 $promiseHasUserDefinedRejectHandler = PromiseHasUserDefinedRejectHandler;
243 +1); 391 $promiseStatus = promiseStatus;
244 break; 392 $promiseValue = promiseValue;
245 case -1: // Rejected
246 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) {
247 // Promise has already been rejected, but had no handler.
248 // Revoke previously triggered reject event.
249 %PromiseRevokeReject(this);
250 }
251 PromiseEnqueue(GET_PRIVATE(this, promiseValue),
252 [onReject, deferred],
253 -1);
254 break;
255 }
256 // Mark this promise as having handler.
257 SET_PRIVATE(this, promiseHasHandler, true);
258 if (DEBUG_IS_ACTIVE) {
259 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this });
260 }
261 return deferred.promise;
262 }
263
264 PromiseCatch = function PromiseCatch(onReject) {
265 return this.then(UNDEFINED, onReject);
266 }
267
268 // Multi-unwrapped chaining with thenable coercion.
269
270 PromiseThen = function PromiseThen(onResolve, onReject) {
271 onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve
272 : PromiseIdResolveHandler;
273 onReject = IS_SPEC_FUNCTION(onReject) ? onReject
274 : PromiseIdRejectHandler;
275 var that = this;
276 var constructor = this.constructor;
277 return %_CallFunction(
278 this,
279 function(x) {
280 x = PromiseCoerce(constructor, x);
281 if (x === that) {
282 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject);
283 return onReject(MakeTypeError('promise_cyclic', [x]));
284 } else if (IsPromise(x)) {
285 return x.then(onResolve, onReject);
286 } else {
287 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve);
288 return onResolve(x);
289 }
290 },
291 onReject,
292 PromiseChain
293 );
294 }
295
296 // Combinators.
297
298 function PromiseCast(x) {
299 // TODO(rossberg): cannot do better until we support @@create.
300 return IsPromise(x) ? x : new this(function(resolve) { resolve(x) });
301 }
302
303 function PromiseAll(iterable) {
304 var deferred = %_CallFunction(this, PromiseDeferred);
305 var resolutions = [];
306 try {
307 var count = 0;
308 var i = 0;
309 for (var value of iterable) {
310 this.resolve(value).then(
311 // Nested scope to get closure over current i.
312 // TODO(arv): Use an inner let binding once available.
313 (function(i) {
314 return function(x) {
315 resolutions[i] = x;
316 if (--count === 0) deferred.resolve(resolutions);
317 }
318 })(i),
319 function(r) { deferred.reject(r); });
320 ++i;
321 ++count;
322 }
323
324 if (count === 0) {
325 deferred.resolve(resolutions);
326 }
327
328 } catch (e) {
329 deferred.reject(e)
330 }
331 return deferred.promise;
332 }
333
334 function PromiseRace(iterable) {
335 var deferred = %_CallFunction(this, PromiseDeferred);
336 try {
337 for (var value of iterable) {
338 this.resolve(value).then(
339 function(x) { deferred.resolve(x) },
340 function(r) { deferred.reject(r) });
341 }
342 } catch (e) {
343 deferred.reject(e)
344 }
345 return deferred.promise;
346 }
347
348
349 // Utility for debugger
350
351 function PromiseHasUserDefinedRejectHandlerRecursive(promise) {
352 var queue = GET_PRIVATE(promise, promiseOnReject);
353 if (IS_UNDEFINED(queue)) return false;
354 for (var i = 0; i < queue.length; i += 2) {
355 if (queue[i] != PromiseIdRejectHandler) return true;
356 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) {
357 return true;
358 }
359 }
360 return false;
361 }
362
363 // Return whether the promise will be handled by a user-defined reject
364 // handler somewhere down the promise chain. For this, we do a depth-first
365 // search for a reject handler that's not the default PromiseIdRejectHandler.
366 PromiseHasUserDefinedRejectHandler =
367 function PromiseHasUserDefinedRejectHandler() {
368 return PromiseHasUserDefinedRejectHandlerRecursive(this);
369 };
370
371 // -------------------------------------------------------------------
372 // Install exported functions.
373
374 %CheckIsBootstrapping();
375 %AddNamedProperty(global, 'Promise', $Promise, DONT_ENUM);
376 %AddNamedProperty(
377 $Promise.prototype, symbolToStringTag, "Promise", DONT_ENUM | READ_ONLY);
378 InstallFunctions($Promise, DONT_ENUM, [
379 "defer", PromiseDeferred,
380 "accept", PromiseResolved,
381 "reject", PromiseRejected,
382 "all", PromiseAll,
383 "race", PromiseRace,
384 "resolve", PromiseCast
385 ]);
386 InstallFunctions($Promise.prototype, DONT_ENUM, [
387 "chain", PromiseChain,
388 "then", PromiseThen,
389 "catch", PromiseCatch
390 ]);
391 393
392 })(); 394 })();
OLDNEW
« no previous file with comments | « src/mirror-debugger.js ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698