Chromium Code Reviews

Side by Side Diff: src/promise.js

Issue 64223010: Harmony promises (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Ignore multiple resolution Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | | Annotate | Revision Log
« no previous file with comments | « src/objects.cc ('k') | src/runtime.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
29 "use strict";
30
31 // This file relies on the fact that the following declaration has been made
32 // in runtime.js:
33 // var $Object = global.Object
34 // var $WeakMap = global.WeakMap
35
36
37 var $Promise = Promise;
38
39
40 //-------------------------------------------------------------------
41
42 // Core functionality.
43
44 // Event queue format: [(value, [(handler, deferred)*])*]
45 // I.e., a list of value/tasks pairs, where the value is a resolution value or
46 // rejection reason, and the tasks are a respective list of handler/deferred
47 // pairs waiting for notification of this value. Each handler is an onResolve or
48 // onReject function provided to the same call of 'when' that produced the
49 // associated deferred.
50 var promiseEvents = new InternalArray;
51
52 // Status values: 0 = pending, +1 = resolved, -1 = rejected
53 var promiseStatus = NEW_PRIVATE("Promise#status");
54 var promiseValue = NEW_PRIVATE("Promise#value");
55 var promiseOnResolve = NEW_PRIVATE("Promise#onResolve");
56 var promiseOnReject = NEW_PRIVATE("Promise#onReject");
57 var promiseRaw = NEW_PRIVATE("Promise#raw");
58
59 function IsPromise(x) {
60 return IS_SPEC_OBJECT(x) && %HasLocalProperty(x, promiseStatus);
61 }
62
63 function Promise(resolver) {
64 if (resolver === promiseRaw) return;
65 var promise = PromiseInit(this);
66 resolver(function(x) { PromiseResolve(promise, x) },
67 function(r) { PromiseReject(promise, r) });
68 }
69
70 function PromiseSet(promise, status, value, onResolve, onReject) {
71 SET_PRIVATE(promise, promiseStatus, status);
72 SET_PRIVATE(promise, promiseValue, value);
73 SET_PRIVATE(promise, promiseOnResolve, onResolve);
74 SET_PRIVATE(promise, promiseOnReject, onReject);
75 return promise;
76 }
77
78 function PromiseInit(promise) {
79 return PromiseSet(promise, 0, UNDEFINED, new InternalArray, new InternalArray)
80 }
81
82 function PromiseDone(promise, status, value, promiseQueue) {
83 if (GET_PRIVATE(promise, promiseStatus) !== 0) return;
84 PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue));
85 PromiseSet(promise, status, value);
86 }
87
88 function PromiseResolve(promise, x) {
89 PromiseDone(promise, +1, x, promiseOnResolve)
90 }
91
92 function PromiseReject(promise, r) {
93 PromiseDone(promise, -1, r, promiseOnReject)
94 }
95
96
97 // Convenience.
98
99 function PromiseDeferred() {
100 if (this === $Promise) {
101 // Optimized case, avoid extra closure.
102 var promise = PromiseInit(new Promise(promiseRaw));
103 return {
104 promise: promise,
105 resolve: function(x) { PromiseResolve(promise, x) },
106 reject: function(r) { PromiseReject(promise, r) }
107 };
108 } else {
109 var result = {};
110 result.promise = new this(function(resolve, reject) {
111 result.resolve = resolve;
112 result.reject = reject;
113 })
114 return result;
115 }
116 }
117
118 function PromiseResolved(x) {
119 if (this === $Promise) {
120 // Optimized case, avoid extra closure.
121 return PromiseSet(new Promise(promiseRaw), +1, x);
122 } else {
123 return new this(function(resolve, reject) { resolve(x) });
124 }
125 }
126
127 function PromiseRejected(r) {
128 if (this === $Promise) {
129 // Optimized case, avoid extra closure.
130 return PromiseSet(new Promise(promiseRaw), -1, r);
131 } else {
132 return new this(function(resolve, reject) { reject(r) });
133 }
134 }
135
136
137 // Simple chaining.
138
139 function PromiseNopHandler() {}
140
141 function PromiseWhen(onResolve, onReject) { // a.k.a. flatMap
142 onResolve = IS_UNDEFINED(onResolve) ? PromiseNopHandler : onResolve;
143 onReject = IS_UNDEFINED(onReject) ? PromiseNopHandler : onReject;
144 var deferred = %_CallFunction(this.constructor, PromiseDeferred);
145 switch (GET_PRIVATE(this, promiseStatus)) {
146 case UNDEFINED:
147 throw MakeTypeError('not_a_promise', [this]);
148 case 0: // Pending
149 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred);
150 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
151 break;
152 case +1: // Resolved
153 PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onResolve, deferred]);
154 break;
155 case -1: // Rejected
156 PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onReject, deferred]);
157 break;
158 }
159 return deferred.promise;
160 }
161
162 function PromiseCatch(onReject) {
163 return this.when(UNDEFINED, onReject);
164 }
165
166 function PromiseEnqueue(value, tasks) {
167 promiseEvents.push(value, tasks);
168 %SetMicrotasksPending(true);
169 }
170
171 function PromiseMicrotasksRunner() {
172 var events = promiseEvents;
173 if (events.length > 0) {
174 promiseEvents = new InternalArray;
175 for (var i = 0; i < events.length; i += 2) {
176 var value = events[i];
177 var tasks = events[i + 1];
178 for (var j = 0; j < tasks.length; j += 2) {
179 var handler = tasks[j];
180 var deferred = tasks[j + 1];
181 try {
182 var result = handler(value);
183 if (result === deferred.promise)
184 throw MakeTypeError('promise_cyclic', [result]);
185 else if (IsPromise(result))
yhirano 2013/11/20 07:05:36 I think statements here correspond to UpdateDeferr
rossberg 2013/11/22 11:03:07 That's because the spec currently does not separat
186 result.when(deferred.resolve, deferred.reject);
187 else
188 deferred.resolve(result);
189 } catch(e) {
190 // TODO(rossberg): perhaps log uncaught exceptions below.
191 try { deferred.reject(e) } catch(e) {}
192 }
193 }
194 }
195 }
196 }
197 RunMicrotasks.runners.push(PromiseMicrotasksRunner);
198
199
200 // Multi-unwrapped chaining with thenable coercion.
201
202 function PromiseThen(onResolve, onReject) {
203 onResolve = IS_UNDEFINED(onResolve) ? PromiseNopHandler : onResolve;
204 var that = this;
205 var constructor = this.constructor;
206 return this.when(
207 function(x) {
208 x = PromiseCoerce(constructor, x);
209 return x === that ? onReject(MakeTypeError('promise_cyclic', [x])) :
210 IsPromise(x) ? x.then(onResolve, onReject) : onResolve(x);
211 },
212 onReject
213 );
214 }
215
216 PromiseCoerce.table = new $WeakMap;
217
218 function PromiseCoerce(constructor, x) {
219 var then;
220 if (IsPromise(x)) {
221 return x;
222 } else if (!IS_NULL_OR_UNDEFINED(x) && %IsCallable(then = x.then)) {
223 if (PromiseCoerce.table.has(x)) {
224 return PromiseCoerce.table.get(x);
225 } else {
226 var deferred = constructor.deferred();
227 PromiseCoerce.table.set(x, deferred.promise);
228 try {
229 %_CallFunction(x, deferred.resolve, deferred.reject, then);
230 } catch(e) {
231 deferred.reject(e);
232 }
233 return deferred.promise;
234 }
235 } else {
236 return x;
237 }
238 }
239
240
241 // Combinators.
242
243 function PromiseCast(x) {
244 // TODO(rossberg): cannot do better until we support @@create.
245 return IsPromise(x) ? x : this.resolved(x);
246 }
247
248 function PromiseAll(values) {
249 var deferred = this.deferred();
250 var count = 0;
251 var resolutions = [];
252 for (var i = 0; i < values.length; ++i) {
253 ++count;
254 this.cast(values[i]).when(
255 function(i, x) {
256 resolutions[i] = x;
257 if (--count === 0) deferred.resolve(resolutions);
258 }.bind(UNDEFINED, i), // TODO(rossberg): use let loop once available
259 function(r) {
260 if (count > 0) { count = 0; deferred.reject(r) }
261 }
262 );
263 }
264 if (count === 0) deferred.resolve(resolutions);
265 return deferred.promise;
266 }
267
268 function PromiseOne(values) { // a.k.a. race
269 var deferred = this.deferred();
270 var done = false;
271 for (var i = 0; i < values.length; ++i) {
272 this.cast(values[i]).when(
273 function(x) { if (!done) { done = true; deferred.resolve(x) } },
274 function(r) { if (!done) { done = true; deferred.reject(r) } }
275 );
276 }
277 return deferred.promise;
278 }
279
280 //-------------------------------------------------------------------
281
282 function SetUpPromise() {
283 %CheckIsBootstrapping()
284 global.Promise = $Promise;
285 InstallFunctions($Promise, DONT_ENUM, [
286 "deferred", PromiseDeferred,
287 "resolved", PromiseResolved,
288 "rejected", PromiseRejected,
289 "all", PromiseAll,
290 "one", PromiseOne,
291 "cast", PromiseCast
292 ]);
293 InstallFunctions($Promise.prototype, DONT_ENUM, [
294 "when", PromiseWhen,
295 "then", PromiseThen,
296 "catch", PromiseCatch
297 ]);
298 }
299
300 SetUpPromise();
OLDNEW
« no previous file with comments | « src/objects.cc ('k') | src/runtime.h » ('j') | no next file with comments »

Powered by Google App Engine