UNPKG

6.21 kBJavaScriptView Raw
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})();