Projekt

Obecné

Profil

Stáhnout (5.8 KB) Statistiky
| Větev: | Revize:
1
'use strict';
2

    
3
var PENDING = 'pending';
4
var SETTLED = 'settled';
5
var FULFILLED = 'fulfilled';
6
var REJECTED = 'rejected';
7
var NOOP = function () {};
8
var isNode = typeof global !== 'undefined' && typeof global.process !== 'undefined' && typeof global.process.emit === 'function';
9

    
10
var asyncSetTimer = typeof setImmediate === 'undefined' ? setTimeout : setImmediate;
11
var asyncQueue = [];
12
var asyncTimer;
13

    
14
function asyncFlush() {
15
	// run promise callbacks
16
	for (var i = 0; i < asyncQueue.length; i++) {
17
		asyncQueue[i][0](asyncQueue[i][1]);
18
	}
19

    
20
	// reset async asyncQueue
21
	asyncQueue = [];
22
	asyncTimer = false;
23
}
24

    
25
function asyncCall(callback, arg) {
26
	asyncQueue.push([callback, arg]);
27

    
28
	if (!asyncTimer) {
29
		asyncTimer = true;
30
		asyncSetTimer(asyncFlush, 0);
31
	}
32
}
33

    
34
function invokeResolver(resolver, promise) {
35
	function resolvePromise(value) {
36
		resolve(promise, value);
37
	}
38

    
39
	function rejectPromise(reason) {
40
		reject(promise, reason);
41
	}
42

    
43
	try {
44
		resolver(resolvePromise, rejectPromise);
45
	} catch (e) {
46
		rejectPromise(e);
47
	}
48
}
49

    
50
function invokeCallback(subscriber) {
51
	var owner = subscriber.owner;
52
	var settled = owner._state;
53
	var value = owner._data;
54
	var callback = subscriber[settled];
55
	var promise = subscriber.then;
56

    
57
	if (typeof callback === 'function') {
58
		settled = FULFILLED;
59
		try {
60
			value = callback(value);
61
		} catch (e) {
62
			reject(promise, e);
63
		}
64
	}
65

    
66
	if (!handleThenable(promise, value)) {
67
		if (settled === FULFILLED) {
68
			resolve(promise, value);
69
		}
70

    
71
		if (settled === REJECTED) {
72
			reject(promise, value);
73
		}
74
	}
75
}
76

    
77
function handleThenable(promise, value) {
78
	var resolved;
79

    
80
	try {
81
		if (promise === value) {
82
			throw new TypeError('A promises callback cannot return that same promise.');
83
		}
84

    
85
		if (value && (typeof value === 'function' || typeof value === 'object')) {
86
			// then should be retrieved only once
87
			var then = value.then;
88

    
89
			if (typeof then === 'function') {
90
				then.call(value, function (val) {
91
					if (!resolved) {
92
						resolved = true;
93

    
94
						if (value === val) {
95
							fulfill(promise, val);
96
						} else {
97
							resolve(promise, val);
98
						}
99
					}
100
				}, function (reason) {
101
					if (!resolved) {
102
						resolved = true;
103

    
104
						reject(promise, reason);
105
					}
106
				});
107

    
108
				return true;
109
			}
110
		}
111
	} catch (e) {
112
		if (!resolved) {
113
			reject(promise, e);
114
		}
115

    
116
		return true;
117
	}
118

    
119
	return false;
120
}
121

    
122
function resolve(promise, value) {
123
	if (promise === value || !handleThenable(promise, value)) {
124
		fulfill(promise, value);
125
	}
126
}
127

    
128
function fulfill(promise, value) {
129
	if (promise._state === PENDING) {
130
		promise._state = SETTLED;
131
		promise._data = value;
132

    
133
		asyncCall(publishFulfillment, promise);
134
	}
135
}
136

    
137
function reject(promise, reason) {
138
	if (promise._state === PENDING) {
139
		promise._state = SETTLED;
140
		promise._data = reason;
141

    
142
		asyncCall(publishRejection, promise);
143
	}
144
}
145

    
146
function publish(promise) {
147
	promise._then = promise._then.forEach(invokeCallback);
148
}
149

    
150
function publishFulfillment(promise) {
151
	promise._state = FULFILLED;
152
	publish(promise);
153
}
154

    
155
function publishRejection(promise) {
156
	promise._state = REJECTED;
157
	publish(promise);
158
	if (!promise._handled && isNode) {
159
		global.process.emit('unhandledRejection', promise._data, promise);
160
	}
161
}
162

    
163
function notifyRejectionHandled(promise) {
164
	global.process.emit('rejectionHandled', promise);
165
}
166

    
167
/**
168
 * @class
169
 */
170
function Promise(resolver) {
171
	if (typeof resolver !== 'function') {
172
		throw new TypeError('Promise resolver ' + resolver + ' is not a function');
173
	}
174

    
175
	if (this instanceof Promise === false) {
176
		throw new TypeError('Failed to construct \'Promise\': Please use the \'new\' operator, this object constructor cannot be called as a function.');
177
	}
178

    
179
	this._then = [];
180

    
181
	invokeResolver(resolver, this);
182
}
183

    
184
Promise.prototype = {
185
	constructor: Promise,
186

    
187
	_state: PENDING,
188
	_then: null,
189
	_data: undefined,
190
	_handled: false,
191

    
192
	then: function (onFulfillment, onRejection) {
193
		var subscriber = {
194
			owner: this,
195
			then: new this.constructor(NOOP),
196
			fulfilled: onFulfillment,
197
			rejected: onRejection
198
		};
199

    
200
		if ((onRejection || onFulfillment) && !this._handled) {
201
			this._handled = true;
202
			if (this._state === REJECTED && isNode) {
203
				asyncCall(notifyRejectionHandled, this);
204
			}
205
		}
206

    
207
		if (this._state === FULFILLED || this._state === REJECTED) {
208
			// already resolved, call callback async
209
			asyncCall(invokeCallback, subscriber);
210
		} else {
211
			// subscribe
212
			this._then.push(subscriber);
213
		}
214

    
215
		return subscriber.then;
216
	},
217

    
218
	catch: function (onRejection) {
219
		return this.then(null, onRejection);
220
	}
221
};
222

    
223
Promise.all = function (promises) {
224
	if (!Array.isArray(promises)) {
225
		throw new TypeError('You must pass an array to Promise.all().');
226
	}
227

    
228
	return new Promise(function (resolve, reject) {
229
		var results = [];
230
		var remaining = 0;
231

    
232
		function resolver(index) {
233
			remaining++;
234
			return function (value) {
235
				results[index] = value;
236
				if (!--remaining) {
237
					resolve(results);
238
				}
239
			};
240
		}
241

    
242
		for (var i = 0, promise; i < promises.length; i++) {
243
			promise = promises[i];
244

    
245
			if (promise && typeof promise.then === 'function') {
246
				promise.then(resolver(i), reject);
247
			} else {
248
				results[i] = promise;
249
			}
250
		}
251

    
252
		if (!remaining) {
253
			resolve(results);
254
		}
255
	});
256
};
257

    
258
Promise.race = function (promises) {
259
	if (!Array.isArray(promises)) {
260
		throw new TypeError('You must pass an array to Promise.race().');
261
	}
262

    
263
	return new Promise(function (resolve, reject) {
264
		for (var i = 0, promise; i < promises.length; i++) {
265
			promise = promises[i];
266

    
267
			if (promise && typeof promise.then === 'function') {
268
				promise.then(resolve, reject);
269
			} else {
270
				resolve(promise);
271
			}
272
		}
273
	});
274
};
275

    
276
Promise.resolve = function (value) {
277
	if (value && typeof value === 'object' && value.constructor === Promise) {
278
		return value;
279
	}
280

    
281
	return new Promise(function (resolve) {
282
		resolve(value);
283
	});
284
};
285

    
286
Promise.reject = function (reason) {
287
	return new Promise(function (resolve, reject) {
288
		reject(reason);
289
	});
290
};
291

    
292
module.exports = Promise;
(1-1/4)