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, thenable) { | 108 function PromiseHandle(value, handler, deferred) { |
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) && thenable) { | 115 else if (IsPromise(result)) |
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)) { | |
123 %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain); | 116 %_CallFunction(result, deferred.resolve, deferred.reject, PromiseChain); |
124 } else { | 117 else |
125 deferred.resolve(result); | 118 deferred.resolve(result); |
126 } | |
127 } catch (exception) { | 119 } catch (exception) { |
128 try { deferred.reject(exception); } catch (e) { } | 120 try { deferred.reject(exception); } catch (e) { } |
129 } finally { | 121 } finally { |
130 %DebugPopPromise(); | 122 %DebugPopPromise(); |
131 } | 123 } |
132 } | 124 } |
133 | 125 |
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 | |
156 function PromiseEnqueue(value, tasks, status) { | 126 function PromiseEnqueue(value, tasks, status) { |
157 var id, name, instrumenting = DEBUG_IS_ACTIVE; | 127 var id, name, instrumenting = DEBUG_IS_ACTIVE; |
158 %EnqueueMicrotask(function() { | 128 %EnqueueMicrotask(function() { |
159 if (instrumenting) { | 129 if (instrumenting) { |
160 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); | 130 %DebugAsyncTaskEvent({ type: "willHandle", id: id, name: name }); |
161 } | 131 } |
162 for (var i = 0; i < tasks.length; i += 3) { | 132 for (var i = 0; i < tasks.length; i += 2) { |
163 // tasks[i: i + 2] consists of the reaction handler, the associated | 133 PromiseHandle(value, tasks[i], tasks[i + 1]) |
164 // deferred object and whether the thenable handling is required. | |
165 PromiseHandle(value, tasks[i], tasks[i + 1], tasks[i + 2]) | |
166 } | 134 } |
167 if (instrumenting) { | 135 if (instrumenting) { |
168 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); | 136 %DebugAsyncTaskEvent({ type: "didHandle", id: id, name: name }); |
169 } | 137 } |
170 }); | 138 }); |
171 if (instrumenting) { | 139 if (instrumenting) { |
172 id = ++lastMicrotaskId; | 140 id = ++lastMicrotaskId; |
173 name = status > 0 ? "Promise.resolve" : "Promise.reject"; | 141 name = status > 0 ? "Promise.resolve" : "Promise.reject"; |
174 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); | 142 %DebugAsyncTaskEvent({ type: "enqueue", id: id, name: name }); |
175 } | 143 } |
176 } | 144 } |
177 | 145 |
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 | |
213 function PromiseIdResolveHandler(x) { return x } | 146 function PromiseIdResolveHandler(x) { return x } |
214 function PromiseIdRejectHandler(r) { throw r } | 147 function PromiseIdRejectHandler(r) { throw r } |
215 | 148 |
216 function PromiseNopResolver() {} | 149 function PromiseNopResolver() {} |
217 | 150 |
218 // ------------------------------------------------------------------- | 151 // ------------------------------------------------------------------- |
219 // Define exported functions. | 152 // Define exported functions. |
220 | 153 |
221 // For bootstrapper. | 154 // For bootstrapper. |
222 | 155 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
284 %PromiseRejectEvent(promise, r, false); | 217 %PromiseRejectEvent(promise, r, false); |
285 } else { | 218 } else { |
286 promise = new this(function(resolve, reject) { reject(r) }); | 219 promise = new this(function(resolve, reject) { reject(r) }); |
287 } | 220 } |
288 return promise; | 221 return promise; |
289 } | 222 } |
290 | 223 |
291 // Simple chaining. | 224 // Simple chaining. |
292 | 225 |
293 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap | 226 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap |
294 return %_CallFunction(this, onResolve, onReject, false, | 227 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve; |
295 PromiseChainInternal); | 228 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject; |
| 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; |
296 } | 259 } |
297 | 260 |
298 function PromiseCatch(onReject) { | 261 function PromiseCatch(onReject) { |
299 return this.then(UNDEFINED, onReject); | 262 return this.then(UNDEFINED, onReject); |
300 } | 263 } |
301 | 264 |
302 // Multi-unwrapped chaining with thenable coercion. | 265 // Multi-unwrapped chaining with thenable coercion. |
303 | 266 |
304 function PromiseThen(onResolve, onReject) { | 267 function PromiseThen(onResolve, onReject) { |
305 onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve | 268 onResolve = IS_SPEC_FUNCTION(onResolve) ? onResolve |
(...skipping 10 matching lines...) Expand all Loading... |
316 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject); | 279 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject); |
317 return onReject(MakeTypeError(kPromiseCyclic, x)); | 280 return onReject(MakeTypeError(kPromiseCyclic, x)); |
318 } else if (IsPromise(x)) { | 281 } else if (IsPromise(x)) { |
319 return x.then(onResolve, onReject); | 282 return x.then(onResolve, onReject); |
320 } else { | 283 } else { |
321 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve); | 284 DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve); |
322 return onResolve(x); | 285 return onResolve(x); |
323 } | 286 } |
324 }, | 287 }, |
325 onReject, | 288 onReject, |
326 true, | 289 PromiseChain |
327 PromiseChainInternal | |
328 ); | 290 ); |
329 } | 291 } |
330 | 292 |
331 // Combinators. | 293 // Combinators. |
332 | 294 |
333 function PromiseCast(x) { | 295 function PromiseCast(x) { |
334 // TODO(rossberg): cannot do better until we support @@create. | 296 // TODO(rossberg): cannot do better until we support @@create. |
335 return IsPromise(x) ? x : new this(function(resolve) { resolve(x) }); | 297 return IsPromise(x) ? x : new this(function(resolve) { resolve(x) }); |
336 } | 298 } |
337 | 299 |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
379 } | 341 } |
380 return deferred.promise; | 342 return deferred.promise; |
381 } | 343 } |
382 | 344 |
383 | 345 |
384 // Utility for debugger | 346 // Utility for debugger |
385 | 347 |
386 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { | 348 function PromiseHasUserDefinedRejectHandlerRecursive(promise) { |
387 var queue = GET_PRIVATE(promise, promiseOnReject); | 349 var queue = GET_PRIVATE(promise, promiseOnReject); |
388 if (IS_UNDEFINED(queue)) return false; | 350 if (IS_UNDEFINED(queue)) return false; |
389 for (var i = 0; i < queue.length; i += 3) { | 351 for (var i = 0; i < queue.length; i += 2) { |
390 if (queue[i] != PromiseIdRejectHandler) return true; | 352 if (queue[i] != PromiseIdRejectHandler) return true; |
391 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { | 353 if (PromiseHasUserDefinedRejectHandlerRecursive(queue[i + 1].promise)) { |
392 return true; | 354 return true; |
393 } | 355 } |
394 } | 356 } |
395 return false; | 357 return false; |
396 } | 358 } |
397 | 359 |
398 // Return whether the promise will be handled by a user-defined reject | 360 // Return whether the promise will be handled by a user-defined reject |
399 // handler somewhere down the promise chain. For this, we do a depth-first | 361 // handler somewhere down the promise chain. For this, we do a depth-first |
(...skipping 28 matching lines...) Expand all Loading... |
428 $promiseResolve = PromiseResolve; | 390 $promiseResolve = PromiseResolve; |
429 $promiseReject = PromiseReject; | 391 $promiseReject = PromiseReject; |
430 $promiseChain = PromiseChain; | 392 $promiseChain = PromiseChain; |
431 $promiseCatch = PromiseCatch; | 393 $promiseCatch = PromiseCatch; |
432 $promiseThen = PromiseThen; | 394 $promiseThen = PromiseThen; |
433 $promiseHasUserDefinedRejectHandler = PromiseHasUserDefinedRejectHandler; | 395 $promiseHasUserDefinedRejectHandler = PromiseHasUserDefinedRejectHandler; |
434 $promiseStatus = promiseStatus; | 396 $promiseStatus = promiseStatus; |
435 $promiseValue = promiseValue; | 397 $promiseValue = promiseValue; |
436 | 398 |
437 }) | 399 }) |
OLD | NEW |