1 | /**
|
2 | * Before reading this source file, open web page [Promises/A+](https://promisesaplus.com).
|
3 | */
|
4 |
|
5 |
|
6 | (function (){
|
7 | /**
|
8 | * This class follows the [Promises/A+](https://promisesaplus.com) and
|
9 | * [ES6](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-promise-objects) spec
|
10 | * with some extra helpers.
|
11 | * @param {Function} executor Function object with two arguments resolve and reject.
|
12 | * The first argument fulfills the promise, the second argument rejects it.
|
13 | * We can call these functions, once our operation is completed.
|
14 | */
|
15 | var Yaku = function (executor) {
|
16 | executor(genSettler(this, $resolved), genSettler(this, $rejected));
|
17 | };
|
18 |
|
19 | try {
|
20 | module.exports = Yaku;
|
21 | } catch (e) {
|
22 | window.Yaku = Yaku;
|
23 | }
|
24 |
|
25 | /**
|
26 | * Appends fulfillment and rejection handlers to the promise,
|
27 | * and returns a new promise resolving to the return value of the called handler.
|
28 | * @param {Function} onFulfilled Optional. Called when the Promise is resolved.
|
29 | * @param {Function} onRejected Optional. Called when the Promise is rejected.
|
30 | * @return {Yaku} It will return a new Yaku which will resolve or reject after
|
31 | * the current Promise.
|
32 | */
|
33 | Yaku.prototype.then = function (onFulfilled, onRejected) {
|
34 | return addHandler(this, new Yaku(function () {}), onFulfilled, onRejected);
|
35 | };
|
36 |
|
37 |
|
38 | /*
|
39 | * All static variable name will begin with `$`. Such as `$rejected`.
|
40 | * @private
|
41 | */
|
42 |
|
43 |
|
44 | /**
|
45 | * These are some static symbolys.
|
46 | * @private
|
47 | */
|
48 |
|
49 | var $rejected = 0;
|
50 |
|
51 | var $resolved = 1;
|
52 |
|
53 | var $pending = 2;
|
54 |
|
55 | // Default state
|
56 | Yaku.prototype._s = $pending;
|
57 |
|
58 |
|
59 | /**
|
60 | * The number of current promises that attach to this Yaku instance.
|
61 | * @private
|
62 | */
|
63 | Yaku.prototype._c = 0;
|
64 |
|
65 |
|
66 | /**
|
67 | * It will produce a settlePromise function to user.
|
68 | * Such as the resolve and reject in this `new Yaku (resolve, reject) ->`.
|
69 | * @private
|
70 | * @param {Yaku} self
|
71 | * @param {Integer} state The value is one of `$pending`, `$resolved` or `$rejected`.
|
72 | * @return {Function} `(value) -> undefined` A resolve or reject function.
|
73 | */
|
74 | function genSettler (self, state) {
|
75 | return function (value) {
|
76 | return settlePromise(self, state, value);
|
77 | };
|
78 | }
|
79 |
|
80 |
|
81 | /**
|
82 | * Link the promise1 to the promise2.
|
83 | * @private
|
84 | * @param {Yaku} p1
|
85 | * @param {Yaku} p2
|
86 | * @param {Function} onFulfilled
|
87 | * @param {Function} onRejected
|
88 | */
|
89 | function addHandler (p1, p2, onFulfilled, onRejected) {
|
90 | // 2.2.1
|
91 | if (typeof onFulfilled === "function") {
|
92 | p2._onFulfilled = onFulfilled;
|
93 | }
|
94 | if (typeof onRejected === "function") {
|
95 | p2._onRejected = onRejected;
|
96 | }
|
97 |
|
98 | // 2.2.6
|
99 | if (p1._s === $pending) {
|
100 | p1[p1._c++] = p2;
|
101 | } else {
|
102 | scheduleHandler(p1, p2);
|
103 | }
|
104 |
|
105 | // # 2.2.7
|
106 | return p2;
|
107 | }
|
108 |
|
109 |
|
110 | /**
|
111 | * Resolve the value returned by onFulfilled or onRejected.
|
112 | * @private
|
113 | * @param {Yaku} p1
|
114 | * @param {Yaku} p2
|
115 | */
|
116 | function scheduleHandler (p1, p2) {
|
117 | return setTimeout(function () {
|
118 | var x, handler = p1._s ? p2._onFulfilled : p2._onRejected;
|
119 |
|
120 | // 2.2.7.3
|
121 | // 2.2.7.4
|
122 | if (handler === void 0) {
|
123 | settlePromise(p2, p1._s, p1._v);
|
124 | return;
|
125 | }
|
126 |
|
127 | try {
|
128 | // 2.2.5
|
129 | x = handler(p1._v);
|
130 | } catch (err) {
|
131 | // 2.2.7.2
|
132 | settlePromise(p2, $rejected, err);
|
133 | return;
|
134 | }
|
135 |
|
136 | // 2.2.7.1
|
137 | settleWithX(p2, x);
|
138 | });
|
139 | }
|
140 |
|
141 |
|
142 | /**
|
143 | * Resolve or reject a promise.
|
144 | * @param {Yaku} p
|
145 | * @param {Integer} state
|
146 | * @param {Any} value
|
147 | */
|
148 | function settlePromise (p, state, value) {
|
149 | // 2.1.2
|
150 | // 2.1.3
|
151 | if (p._s !== $pending) return;
|
152 |
|
153 | // 2.1.1.1
|
154 | p._s = state;
|
155 | p._v = value;
|
156 |
|
157 | var i = 0,
|
158 | len = p._c;
|
159 |
|
160 | // 2.2.2
|
161 | // 2.2.3
|
162 | while (i < len) {
|
163 | // 2.2.4
|
164 | scheduleHandler(p, p[i++]);
|
165 | }
|
166 |
|
167 | return p;
|
168 | }
|
169 |
|
170 |
|
171 | /**
|
172 | * Resolve or reject primise with value x. The x can also be a thenable.
|
173 | * @private
|
174 | * @param {Yaku} p
|
175 | * @param {Any | Thenable} x A normal value or a thenable.
|
176 | */
|
177 | function settleWithX (p, x) {
|
178 | // 2.3.1
|
179 | if (x === p && x) {
|
180 | settlePromise(p, $rejected, new TypeError("promise_circular_chain"));
|
181 | return;
|
182 | }
|
183 |
|
184 | // 2.3.2
|
185 | // 2.3.3
|
186 | var xthen, type = typeof x;
|
187 | if (x !== null && (type === "function" || type === "object")) {
|
188 | try {
|
189 | // 2.3.2.1
|
190 | xthen = x.then;
|
191 | } catch (err) {
|
192 | // 2.3.3.2
|
193 | settlePromise(p, $rejected, err);
|
194 | return;
|
195 | }
|
196 | if (typeof xthen === "function") {
|
197 | settleXthen(p, x, xthen);
|
198 | } else {
|
199 | // 2.3.3.4
|
200 | settlePromise(p, $resolved, x);
|
201 | }
|
202 | } else {
|
203 | // 2.3.4
|
204 | settlePromise(p, $resolved, x);
|
205 | }
|
206 | return p;
|
207 | }
|
208 |
|
209 |
|
210 | /**
|
211 | * Resolve then with its promise.
|
212 | * @private
|
213 | * @param {Yaku} p
|
214 | * @param {Thenable} x
|
215 | * @param {Function} xthen
|
216 | */
|
217 |
|
218 | function settleXthen (p, x, xthen) {
|
219 | try {
|
220 | // 2.3.3.3
|
221 | xthen.call(x, function (y) {
|
222 | // 2.3.3.3.3
|
223 | if (!x) return;
|
224 | x = null;
|
225 |
|
226 | // 2.3.3.3.1
|
227 | settleWithX(p, y);
|
228 | }, function (r) {
|
229 | // 2.3.3.3.3
|
230 | if (!x) return;
|
231 | x = null;
|
232 |
|
233 | // 2.3.3.3.2
|
234 | settlePromise(p, $rejected, r);
|
235 | });
|
236 | } catch (err) {
|
237 | // 2.3.3.3.4.1
|
238 | if (x) {
|
239 | // 2.3.3.3.4.2
|
240 | settlePromise(p, $rejected, err);
|
241 | x = null;
|
242 | }
|
243 | }
|
244 | }
|
245 |
|
246 | })();
|