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

Side by Side Diff: src/promise.js

Issue 64223010: Harmony promises (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: "Renamed .when to .chain; Dmitry's comments" Created 7 years 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 | 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 'chain' 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 // TODO(rossberg): current draft makes exception from this call asynchronous,
69 // but that's probably a mistake.
70 }
71
72 function PromiseSet(promise, status, value, onResolve, onReject) {
73 SET_PRIVATE(promise, promiseStatus, status);
74 SET_PRIVATE(promise, promiseValue, value);
75 SET_PRIVATE(promise, promiseOnResolve, onResolve);
76 SET_PRIVATE(promise, promiseOnReject, onReject);
77 return promise;
78 }
79
80 function PromiseInit(promise) {
81 return PromiseSet(promise, 0, UNDEFINED, new InternalArray, new InternalArray)
82 }
83
84 function PromiseDone(promise, status, value, promiseQueue) {
85 if (GET_PRIVATE(promise, promiseStatus) !== 0) return;
86 PromiseEnqueue(value, GET_PRIVATE(promise, promiseQueue));
87 PromiseSet(promise, status, value);
88 }
89
90 function PromiseResolve(promise, x) {
91 PromiseDone(promise, +1, x, promiseOnResolve)
92 }
93
94 function PromiseReject(promise, r) {
95 PromiseDone(promise, -1, r, promiseOnReject)
96 }
97
98
99 // Convenience.
100
101 function PromiseDeferred() {
102 if (this === $Promise) {
103 // Optimized case, avoid extra closure.
104 var promise = PromiseInit(new Promise(promiseRaw));
105 return {
106 promise: promise,
107 resolve: function(x) { PromiseResolve(promise, x) },
108 reject: function(r) { PromiseReject(promise, r) }
109 };
110 } else {
111 var result = {};
112 result.promise = new this(function(resolve, reject) {
113 result.resolve = resolve;
114 result.reject = reject;
115 })
116 return result;
117 }
118 }
119
120 function PromiseResolved(x) {
121 if (this === $Promise) {
122 // Optimized case, avoid extra closure.
123 return PromiseSet(new Promise(promiseRaw), +1, x);
124 } else {
125 return new this(function(resolve, reject) { resolve(x) });
126 }
127 }
128
129 function PromiseRejected(r) {
130 if (this === $Promise) {
131 // Optimized case, avoid extra closure.
132 return PromiseSet(new Promise(promiseRaw), -1, r);
133 } else {
134 return new this(function(resolve, reject) { reject(r) });
135 }
136 }
137
138
139 // Simple chaining.
140
141 function PromiseIdResolveHandler(x) { return x }
142 function PromiseIdRejectHandler(r) { throw r }
143
144 function PromiseChain(onResolve, onReject) { // a.k.a. flatMap
145 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
146 onReject = IS_UNDEFINED(onReject) ? PromiseIdRejectHandler : onReject;
147 var deferred = %_CallFunction(this.constructor, PromiseDeferred);
148 switch (GET_PRIVATE(this, promiseStatus)) {
149 case UNDEFINED:
150 throw MakeTypeError('not_a_promise', [this]);
151 case 0: // Pending
152 GET_PRIVATE(this, promiseOnResolve).push(onResolve, deferred);
153 GET_PRIVATE(this, promiseOnReject).push(onReject, deferred);
154 break;
155 case +1: // Resolved
156 PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onResolve, deferred]);
157 break;
158 case -1: // Rejected
159 PromiseEnqueue(GET_PRIVATE(this, promiseValue), [onReject, deferred]);
160 break;
161 }
162 return deferred.promise;
163 }
164
165 function PromiseCatch(onReject) {
166 return this.chain(UNDEFINED, onReject);
167 }
168
169 function PromiseEnqueue(value, tasks) {
170 promiseEvents.push(value, tasks);
171 %SetMicrotaskPending(true);
172 }
173
174 function PromiseMicrotaskRunner() {
175 var events = promiseEvents;
176 if (events.length > 0) {
177 promiseEvents = new InternalArray;
178 for (var i = 0; i < events.length; i += 2) {
179 var value = events[i];
180 var tasks = events[i + 1];
181 for (var j = 0; j < tasks.length; j += 2) {
182 var handler = tasks[j];
183 var deferred = tasks[j + 1];
184 try {
185 var result = handler(value);
186 if (result === deferred.promise)
187 throw MakeTypeError('promise_cyclic', [result]);
188 else if (IsPromise(result))
189 result.chain(deferred.resolve, deferred.reject);
190 else
191 deferred.resolve(result);
192 } catch(e) {
193 // TODO(rossberg): perhaps log uncaught exceptions below.
194 try { deferred.reject(e) } catch(e) {}
195 }
196 }
197 }
198 }
199 }
200 RunMicrotasks.runners.push(PromiseMicrotaskRunner);
201
202
203 // Multi-unwrapped chaining with thenable coercion.
204
205 function PromiseThen(onResolve, onReject) {
206 onResolve = IS_UNDEFINED(onResolve) ? PromiseIdResolveHandler : onResolve;
207 var that = this;
208 var constructor = this.constructor;
209 return this.chain(
210 function(x) {
211 x = PromiseCoerce(constructor, x);
212 return x === that ? onReject(MakeTypeError('promise_cyclic', [x])) :
213 IsPromise(x) ? x.then(onResolve, onReject) : onResolve(x);
214 },
215 onReject
216 );
217 }
218
219 PromiseCoerce.table = new $WeakMap;
220
221 function PromiseCoerce(constructor, x) {
222 var then;
223 if (IsPromise(x)) {
224 return x;
225 } else if (!IS_NULL_OR_UNDEFINED(x) && %IsCallable(then = x.then)) {
226 if (PromiseCoerce.table.has(x)) {
227 return PromiseCoerce.table.get(x);
228 } else {
229 var deferred = constructor.deferred();
230 PromiseCoerce.table.set(x, deferred.promise);
231 try {
232 %_CallFunction(x, deferred.resolve, deferred.reject, then);
233 } catch(e) {
234 deferred.reject(e);
235 }
236 return deferred.promise;
237 }
238 } else {
239 return x;
240 }
241 }
242
243
244 // Combinators.
245
246 function PromiseCast(x) {
247 // TODO(rossberg): cannot do better until we support @@create.
248 return IsPromise(x) ? x : this.resolved(x);
249 }
250
251 function PromiseAll(values) {
252 var deferred = this.deferred();
253 var resolutions = [];
254 var count = values.length;
255 if (count === 0) {
256 deferred.resolve(resolutions);
257 } else {
258 for (var i = 0; i < values.length; ++i) {
259 this.cast(values[i]).chain(
260 function(i, x) {
261 resolutions[i] = x;
262 if (--count === 0) deferred.resolve(resolutions);
263 }.bind(UNDEFINED, i), // TODO(rossberg): use let loop once available
264 function(r) {
265 if (count > 0) { count = 0; deferred.reject(r) }
266 }
267 );
268 }
269 }
270 return deferred.promise;
271 }
272
273 function PromiseOne(values) { // a.k.a. race
274 var deferred = this.deferred();
275 var done = false;
276 for (var i = 0; i < values.length; ++i) {
277 this.cast(values[i]).chain(
278 function(x) { if (!done) { done = true; deferred.resolve(x) } },
279 function(r) { if (!done) { done = true; deferred.reject(r) } }
280 );
281 }
282 return deferred.promise;
283 }
284
285 //-------------------------------------------------------------------
286
287 function SetUpPromise() {
288 %CheckIsBootstrapping()
289 global.Promise = $Promise;
290 InstallFunctions($Promise, DONT_ENUM, [
291 "deferred", PromiseDeferred,
292 "resolved", PromiseResolved,
293 "rejected", PromiseRejected,
294 "all", PromiseAll,
295 "one", PromiseOne,
296 "cast", PromiseCast
297 ]);
298 InstallFunctions($Promise.prototype, DONT_ENUM, [
299 "chain", PromiseChain,
300 "then", PromiseThen,
301 "catch", PromiseCatch
302 ]);
303 }
304
305 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
This is Rietveld 408576698