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 var $promiseCreate; | 5 var $promiseCreate; |
6 var $promiseResolve; | 6 var $promiseResolve; |
7 var $promiseReject; | 7 var $promiseReject; |
8 var $promiseChain; | 8 var $promiseChain; |
9 var $promiseCatch; | 9 var $promiseCatch; |
10 var $promiseThen; | 10 var $promiseThen; |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
98 %_CallFunction(x, deferred.resolve, deferred.reject, then); | 98 %_CallFunction(x, deferred.resolve, deferred.reject, then); |
99 } catch(r) { | 99 } catch(r) { |
100 deferred.reject(r); | 100 deferred.reject(r); |
101 } | 101 } |
102 return deferred.promise; | 102 return deferred.promise; |
103 } | 103 } |
104 } | 104 } |
105 return x; | 105 return x; |
106 } | 106 } |
107 | 107 |
108 function PromiseHandle(value, handler, deferred) { | 108 function PromiseHandle(value, handler, deferred, thenable) { |
109 try { | 109 try { |
110 %DebugPushPromise(deferred.promise, PromiseHandle); | 110 %DebugPushPromise(deferred.promise, PromiseHandle); |
111 DEBUG_PREPARE_STEP_IN_IF_STEPPING(handler); | 111 DEBUG_PREPARE_STEP_IN_IF_STEPPING(handler); |
112 var result = handler(value); | 112 var result = handler(value); |
113 if (result === deferred.promise) | 113 if (result === deferred.promise) { |
114 throw MakeTypeError(kPromiseCyclic, result); | 114 throw MakeTypeError(kPromiseCyclic, result); |
115 else if (IsPromise(result)) | 115 } else if (IsPromise(result) && thenable) { |
| 116 var then = result.then; |
| 117 if (IS_SPEC_FUNCTION(then)) { |
| 118 PromiseCallThenInSeparateTask(result, deferred, then); |
| 119 } else { |
| 120 deferred.resolve(result); |
| 121 } |
| 122 } else if (IsPromise(result)) { |
116 %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain); | 123 %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain); |
117 else | 124 } else { |
118 deferred.resolve(result); | 125 deferred.resolve(result); |
| 126 } |
119 } catch (exception) { | 127 } catch (exception) { |
120 try { deferred.reject(exception); } catch (e) { } | 128 try { deferred.reject(exception); } catch (e) { } |
121 } finally { | 129 } finally { |
122 %DebugPopPromise(); | 130 %DebugPopPromise(); |
123 } | 131 } |
124 } | 132 } |
125 | 133 |
| 134 function PromiseCallThenInSeparateTask(result, deferred, then) { |
| 135 var id, name, instrumenting = DEBUG_IS_ACTIVE; |
| 136 %EnqueueMicrotask(function() { |
| 137 if (instrumenting) { |
| 138 %DebugAsyncTaskEvent({ type: "willCall", id: id, name: name }); |
| 139 } |
| 140 try { |
| 141 %_CallFunction(result, deferred.resolve, deferred.reject, then); |
| 142 } catch(exception) { |
| 143 try { deferred.reject(exception); } catch (e) { } |
| 144 } |
| 145 if (instrumenting) { |
| 146 %DebugAsyncTaskEvent({ type: "didCall", id: id, name: name }); |
| 147 } |
| 148 }); |
| 149 if (instrumenting) { |
| 150 id = ++lastMicrotaskId; |
| 151 name = "Promise.prototype.then"; |
| 152 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); |
| 153 } |
| 154 } |
| 155 |
126 function PromiseEnqueue(value, tasks, status) { | 156 function PromiseEnqueue(value, tasks, status) { |
127 var id, name, instrumenting = DEBUG_IS_ACTIVE; | 157 var id, name, instrumenting = DEBUG_IS_ACTIVE; |
128 %EnqueueMicrotask(function() { | 158 %EnqueueMicrotask(function() { |
129 if (instrumenting) { | 159 if (instrumenting) { |
130 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); | 160 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); |
131 } | 161 } |
132 for (var i = 0; i < tasks.length; i += 2) { | 162 for (var i = 0; i < tasks.length; i += 3) { |
133 PromiseHandle(value, tasks[i], tasks[i + 1]) | 163 // tasks[i: i + 2] consists of the reaction handler, the associated |
| 164 // deferred object and whether the thenable handling is required. |
| 165 PromiseHandle(value, tasks[i], tasks[i + 1], tasks[i + 2]) |
134 } | 166 } |
135 if (instrumenting) { | 167 if (instrumenting) { |
136 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); | 168 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); |
137 } | 169 } |
138 }); | 170 }); |
139 if (instrumenting) { | 171 if (instrumenting) { |
140 id = ++lastMicrotaskId; | 172 id = ++lastMicrotaskId; |
141 name = status > 0 ? "Promise.resolve" : "Promise.reject"; | 173 name = status > 0 ? "Promise.resolve" : "Promise.reject"; |
142 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); | 174 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); |
143 } | 175 } |
144 } | 176 } |
145 | 177 |
| 178 function PromiseChainInternal(onResolve, onReject, thenable) { |
| 179 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; |
| 180 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; |
| 181 var deferred = %_CallFunction(this.constructor, PromiseDeferred); |
| 182 switch (GET_PRIVATE(this, promiseStatus)) { |
| 183 case UNDEFINED: |
| 184 throw MakeTypeError(kNotAPromise, this); |
| 185 case 0: // Pending |
| 186 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred, thenable); |
| 187 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred, thenable); |
| 188 break; |
| 189 case +1: // Resolved |
| 190 PromiseEnqueue(GET_PRIVATE(this, promiseValue), |
| 191 [onResolve, deferred, thenable], |
| 192 +1); |
| 193 break; |
| 194 case -1: // Rejected |
| 195 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) { |
| 196 // Promise has already been rejected, but had no handler. |
| 197 // Revoke previously triggered reject event. |
| 198 %PromiseRevokeReject(this); |
| 199 } |
| 200 PromiseEnqueue(GET_PRIVATE(this, promiseValue), |
| 201 [onReject, deferred, thenable], |
| 202 -1); |
| 203 break; |
| 204 } |
| 205 // Mark this promise as having handler. |
| 206 SET_PRIVATE(this, promiseHasHandler, true); |
| 207 if (DEBUG_IS_ACTIVE) { |
| 208 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); |
| 209 } |
| 210 return deferred.promise; |
| 211 } |
| 212 |
146 function PromiseIdResolveHandler(x) { return x } | 213 function PromiseIdResolveHandler(x) { return x } |
147 function PromiseIdRejectHandler(r) { throw r } | 214 function PromiseIdRejectHandler(r) { throw r } |
148 | 215 |
149 function PromiseNopResolver() {} | 216 function PromiseNopResolver() {} |
150 | 217 |
151 // ------------------------------------------------------------------- | 218 // ------------------------------------------------------------------- |
152 // Define exported functions. | 219 // Define exported functions. |
153 | 220 |
154 // For bootstrapper. | 221 // For bootstrapper. |
155 | 222 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
217 %PromiseRejectEvent(promise, r, false); | 284 %PromiseRejectEvent(promise, r, false); |
218 } else { | 285 } else { |
219 promise = new this(function(resolve, reject) { reject(r) }); | 286 promise = new this(function(resolve, reject) { reject(r) }); |
220 } | 287 } |
221 return promise; | 288 return promise; |
222 } | 289 } |
223 | 290 |
224 // Simple chaining. | 291 // Simple chaining. |
225 | 292 |
226 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap | 293 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap |
227 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; | 294 return %_CallFunction(this, onResolve, onReject, false, |
228 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; | 295 PromiseChainInternal); |
229 var deferred = %_CallFunction(this.constructor, PromiseDeferred); | |
230 switch (GET_PRIVATE(this, promiseStatus)) { | |
231 case UNDEFINED: | |
232 throw MakeTypeError(kNotAPromise, this); | |
233 case 0: // Pending | |
234 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred); | |
235 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred); | |
236 break; | |
237 case +1: // Resolved | |
238 PromiseEnqueue(GET_PRIVATE(this, promiseValue), | |
239 [onResolve, deferred], | |
240 +1); | |
241 break; | |
242 case -1: // Rejected | |
243 if (!HAS_DEFINED_PRIVATE(this, promiseHasHandler)) { | |
244 // Promise has already been rejected, but had no handler. | |
245 // Revoke previously triggered reject event. | |
246 %PromiseRevokeReject(this); | |
247 } | |
248 PromiseEnqueue(GET_PRIVATE(this, promiseValue), | |
249 [onReject, deferred], | |
250 -1); | |
251 break; | |
252 } | |
253 // Mark this promise as having handler. | |
254 SET_PRIVATE(this, promiseHasHandler, true); | |
255 if (DEBUG_IS_ACTIVE) { | |
256 %DebugPromiseEvent({ promise: deferred.promise, parentPromise: this }); | |
257 } | |
258 return deferred.promise; | |
259 } | 296 } |
260 | 297 |
261 function PromiseCatch(onReject) { | 298 function PromiseCatch(onReject) { |
262 return this.then(UNDEFINED, onReject); | 299 return this.then(UNDEFINED, onReject); |
263 } | 300 } |
264 | 301 |
265 // Multi-unwrapped chaining with thenable coercion. | 302 // Multi-unwrapped chaining with thenable coercion. |
266 | 303 |
267 function PromiseThen(onResolve, onReject) { | 304 function PromiseThen(onResolve, onReject) { |
268 onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve | 305 onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve |
(...skipping 10 matching lines...) Expand all Loading... |
279 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject); | 316 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject); |
280 return onReject(MakeTypeError(kPromiseCyclic, x)); | 317 return onReject(MakeTypeError(kPromiseCyclic, x)); |
281 } else if (IsPromise(x)) { | 318 } else if (IsPromise(x)) { |
282 return x.then(onResolve, onReject); | 319 return x.then(onResolve, onReject); |
283 } else { | 320 } else { |
284 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve); | 321 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve); |
285 return onResolve(x); | 322 return onResolve(x); |
286 } | 323 } |
287 }, | 324 }, |
288 onReject, | 325 onReject, |
289 PromiseChain | 326 true, |
| 327 PromiseChainInternal |
290 ); | 328 ); |
291 } | 329 } |
292 | 330 |
293 // Combinators. | 331 // Combinators. |
294 | 332 |
295 function PromiseCast(x) { | 333 function PromiseCast(x) { |
296 // TODO(rossberg): cannot do better until we support @@create. | 334 // TODO(rossberg): cannot do better until we support @@create. |
297 return IsPromise(x) ? x : new this(function(resolve) { resolve(x) }); | 335 return IsPromise(x) ? x : new this(function(resolve) { resolve(x) }); |
298 } | 336 } |
299 | 337 |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
341 } | 379 } |
342 return deferred.promise; | 380 return deferred.promise; |
343 } | 381 } |
344 | 382 |
345 | 383 |
346 // Utility for debugger | 384 // Utility for debugger |
347 | 385 |
348 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { | 386 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { |
349 var queue = GET_PRIVATE(promise, promiseOnReject); | 387 var queue = GET_PRIVATE(promise, promiseOnReject); |
350 if (IS_UNDEFINED(queue)) return false; | 388 if (IS_UNDEFINED(queue)) return false; |
351 for (var i = 0; i < queue.length; i += 2) { | 389 for (var i = 0; i < queue.length; i += 3) { |
352 if (queue[i] != PromiseIdRejectHandler) return true; | 390 if (queue[i] != PromiseIdRejectHandler) return true; |
353 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { | 391 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { |
354 return true; | 392 return true; |
355 } | 393 } |
356 } | 394 } |
357 return false; | 395 return false; |
358 } | 396 } |
359 | 397 |
360 // Return whether the promise will be handled by a user-defined reject | 398 // Return whether the promise will be handled by a user-defined reject |
361 // handler somewhere down the promise chain. For this, we do a depth-first | 399 // handler somewhere down the promise chain. For this, we do a depth-first |
(...skipping 28 matching lines...) Expand all Loading... |
390 $promiseResolve = PromiseResolve; | 428 $promiseResolve = PromiseResolve; |
391 $promiseReject = PromiseReject; | 429 $promiseReject = PromiseReject; |
392 $promiseChain = PromiseChain; | 430 $promiseChain = PromiseChain; |
393 $promiseCatch = PromiseCatch; | 431 $promiseCatch = PromiseCatch; |
394 $promiseThen = PromiseThen; | 432 $promiseThen = PromiseThen; |
395 $promiseHasUserDefinedRejectHandler = PromiseHasUserDefinedRejectHandler; | 433 $promiseHasUserDefinedRejectHandler = PromiseHasUserDefinedRejectHandler; |
396 $promiseStatus = promiseStatus; | 434 $promiseStatus = promiseStatus; |
397 $promiseValue = promiseValue; | 435 $promiseValue = promiseValue; |
398 | 436 |
399 }) | 437 }) |
OLD | NEW |