2 * Copyright (C) 2015 Yusuke Suzuki <utatane.tea@gmail.com>.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 function isPromise(promise
)
30 return @isObject(promise
) && !!promise
.@promiseState
;
33 function newPromiseReaction(capability
, handler
)
38 @capabilities: capability
,
43 function newPromiseDeferred()
47 return @newPromiseCapability(@Promise
);
50 function newPromiseCapability(constructor)
54 // FIXME: Check isConstructor(constructor).
55 if (typeof constructor !== "function")
56 throw new @TypeError("promise capability requires a constructor function");
58 var promiseCapability
= {
64 function executor(resolve
, reject
)
66 if (promiseCapability
.@resolve
!== undefined)
67 throw new @TypeError("resolve function is already set");
68 if (promiseCapability
.@reject
!== undefined)
69 throw new @TypeError("reject function is already set");
71 promiseCapability
.@resolve
= resolve
;
72 promiseCapability
.@reject
= reject
;
75 var promise
= new constructor(executor
);
77 if (typeof promiseCapability
.@resolve
!== "function")
78 throw new @TypeError("executor did not take a resolve function");
80 if (typeof promiseCapability
.@reject
!== "function")
81 throw new @TypeError("executor did not take a reject function");
83 promiseCapability
.@promise
= promise
;
85 return promiseCapability
;
88 function triggerPromiseReactions(reactions
, argument
)
92 for (var index
= 0, length
= reactions
.length
; index
< length
; ++index
)
93 @enqueueJob(@promiseReactionJob
, [reactions
[index
], argument
]);
96 function rejectPromise(promise
, reason
)
100 var reactions
= promise
.@promiseRejectReactions
;
101 promise
.@promiseResult
= reason
;
102 promise
.@promiseFulfillReactions
= undefined;
103 promise
.@promiseRejectReactions
= undefined;
104 promise
.@promiseState
= @promiseRejected
;
105 @triggerPromiseReactions(reactions
, reason
);
108 function fulfillPromise(promise
, value
)
112 var reactions
= promise
.@promiseFulfillReactions
;
113 promise
.@promiseResult
= value
;
114 promise
.@promiseFulfillReactions
= undefined;
115 promise
.@promiseRejectReactions
= undefined;
116 promise
.@promiseState
= @promiseFulfilled
;
117 @triggerPromiseReactions(reactions
, value
);
120 function createResolvingFunctions(promise
)
124 var alreadyResolved
= false;
126 var resolve = function (resolution
) {
129 alreadyResolved
= true;
131 if (resolution
=== promise
)
132 return @rejectPromise(promise
, new @TypeError("Resolve a promise with itself"));
134 if (!@isObject(resolution
))
135 return @fulfillPromise(promise
, resolution
);
139 then
= resolution
.then
;
141 return @rejectPromise(promise
, error
);
144 if (typeof then
!== 'function')
145 return @fulfillPromise(promise
, resolution
);
147 @enqueueJob(@promiseResolveThenableJob
, [promise
, resolution
, then
]);
152 var reject = function (reason
) {
155 alreadyResolved
= true;
157 return @rejectPromise(promise
, reason
);
166 function promiseReactionJob(reaction
, argument
)
170 var promiseCapability
= reaction
.@capabilities
;
174 result
= reaction
.@handler
.@call(undefined, argument
);
176 return promiseCapability
.@reject
.@call(undefined, error
);
179 return promiseCapability
.@resolve
.@call(undefined, result
);
182 function promiseResolveThenableJob(promiseToResolve
, thenable
, then
)
186 var resolvingFunctions
= @createResolvingFunctions(promiseToResolve
);
189 return then
.@call(thenable
, resolvingFunctions
.@resolve
, resolvingFunctions
.@reject
);
191 return resolvingFunctions
.@reject
.@call(undefined, error
);
195 function initializePromise(executor
)
199 if (typeof executor
!== 'function')
200 throw new @TypeError("Promise constructor takes a function argument");
202 this.@promiseState
= @promisePending
;
203 this.@promiseFulfillReactions
= [];
204 this.@promiseRejectReactions
= [];
206 var resolvingFunctions
= @createResolvingFunctions(this);
208 executor(resolvingFunctions
.@resolve
, resolvingFunctions
.@reject
);
210 return resolvingFunctions
.@reject
.@call(undefined, error
);