thoughts/node_modules/mongoose/node_modules/mpromise/lib/promise.js
node_modules/mongoose/node_modules/mpromise/lib/promise.jsRaw
1'use strict';
2var util = require('util');
3var EventEmitter = require('events').EventEmitter;
4function toArray(arr, start, end) {
5 return Array.prototype.slice.call(arr, start, end)
6}
7function strongUnshift(x, arrLike) {
8 var arr = toArray(arrLike);
9 arr.unshift(x);
10 return arr;
11}
12
13
14/**
15 * MPromise constructor.
16 *
17 * _NOTE: The success and failure event names can be overridden by setting `Promise.SUCCESS` and `Promise.FAILURE` respectively._
18 *
19 * @param {Function} back a function that accepts `fn(err, ...){}` as signature
20 * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
21 * @event `reject`: Emits when the promise is rejected (event name may be overridden)
22 * @event `fulfill`: Emits when the promise is fulfilled (event name may be overridden)
23 * @api public
24 */
25function Promise(back) {
26 this.emitter = new EventEmitter();
27 this.emitted = {};
28 this.ended = false;
29 if ('function' == typeof back) {
30 this.ended = true;
31 this.onResolve(back);
32 }
33}
34
35
36/*
37 * Module exports.
38 */
39module.exports = Promise;
40
41
42/*!
43 * event names
44 */
45Promise.SUCCESS = 'fulfill';
46Promise.FAILURE = 'reject';
47
48
49/**
50 * Adds `listener` to the `event`.
51 *
52 * If `event` is either the success or failure event and the event has already been emitted, the`listener` is called immediately and passed the results of the original emitted event.
53 *
54 * @param {String} event
55 * @param {Function} callback
56 * @return {MPromise} this
57 * @api private
58 */
59Promise.prototype.on = function (event, callback) {
60 if (this.emitted[event])
61 callback.apply(undefined, this.emitted[event]);
62 else
63 this.emitter.on(event, callback);
64
65 return this;
66};
67
68
69/**
70 * Keeps track of emitted events to run them on `on`.
71 *
72 * @api private
73 */
74Promise.prototype.safeEmit = function (event) {
75 // ensures a promise can't be fulfill() or reject() more than once
76 if (event == Promise.SUCCESS || event == Promise.FAILURE) {
77 if (this.emitted[Promise.SUCCESS] || this.emitted[Promise.FAILURE]) {
78 return this;
79 }
80 this.emitted[event] = toArray(arguments, 1);
81 }
82
83 this.emitter.emit.apply(this.emitter, arguments);
84 return this;
85};
86
87
88/**
89 * @api private
90 */
91Promise.prototype.hasRejectListeners = function () {
92 return EventEmitter.listenerCount(this.emitter, Promise.FAILURE) > 0;
93};
94
95
96/**
97 * Fulfills this promise with passed arguments.
98 *
99 * If this promise has already been fulfilled or rejected, no action is taken.
100 *
101 * @api public
102 */
103Promise.prototype.fulfill = function () {
104 return this.safeEmit.apply(this, strongUnshift(Promise.SUCCESS, arguments));
105};
106
107
108/**
109 * Rejects this promise with `reason`.
110 *
111 * If this promise has already been fulfilled or rejected, no action is taken.
112 *
113 * @api public
114 * @param {Object|String} reason
115 * @return {MPromise} this
116 */
117Promise.prototype.reject = function (reason) {
118 if (this.ended && !this.hasRejectListeners())
119 throw reason;
120 return this.safeEmit(Promise.FAILURE, reason);
121};
122
123
124/**
125 * Resolves this promise to a rejected state if `err` is passed or
126 * fulfilled state if no `err` is passed.
127 *
128 * @param {Error} [err] error or null
129 * @param {Object} [val] value to fulfill the promise with
130 * @api public
131 */
132Promise.prototype.resolve = function (err, val) {
133 if (err) return this.reject(err);
134 return this.fulfill(val);
135};
136
137
138/**
139 * Adds a listener to the SUCCESS event.
140 *
141 * @return {MPromise} this
142 * @api public
143 */
144Promise.prototype.onFulfill = function (fn) {
145 if (!fn) return this;
146 if ('function' != typeof fn) throw new TypeError("fn should be a function");
147 return this.on(Promise.SUCCESS, fn);
148};
149
150
151/**
152 * Adds a listener to the FAILURE event.
153 *
154 * @return {MPromise} this
155 * @api public
156 */
157Promise.prototype.onReject = function (fn) {
158 if (!fn) return this;
159 if ('function' != typeof fn) throw new TypeError("fn should be a function");
160 return this.on(Promise.FAILURE, fn);
161};
162
163
164/**
165 * Adds a single function as a listener to both SUCCESS and FAILURE.
166 *
167 * It will be executed with traditional node.js argument position:
168 * function (err, args...) {}
169 *
170 * Also marks the promise as `end`ed, since it's the common use-case, and yet has no
171 * side effects unless `fn` is undefined or null.
172 *
173 * @param {Function} fn
174 * @return {MPromise} this
175 */
176Promise.prototype.onResolve = function (fn) {
177 if (!fn) return this;
178 if ('function' != typeof fn) throw new TypeError("fn should be a function");
179 this.on(Promise.FAILURE, function (err) { fn.call(this, err); });
180 this.on(Promise.SUCCESS, function () { fn.apply(this, strongUnshift(null, arguments)); });
181 return this;
182};
183
184
185/**
186 * Creates a new promise and returns it. If `onFulfill` or
187 * `onReject` are passed, they are added as SUCCESS/ERROR callbacks
188 * to this promise after the next tick.
189 *
190 * Conforms to [promises/A+](https://github.com/promises-aplus/promises-spec) specification. Read for more detail how to use this method.
191 *
192 * ####Example:
193 *
194 * var p = new Promise;
195 * p.then(function (arg) {
196 * return arg + 1;
197 * }).then(function (arg) {
198 * throw new Error(arg + ' is an error!');
199 * }).then(null, function (err) {
200 * assert.ok(err instanceof Error);
201 * assert.equal('2 is an error', err.message);
202 * });
203 * p.complete(1);
204 *
205 * @see promises-A+ https://github.com/promises-aplus/promises-spec
206 * @param {Function} onFulfill
207 * @param {Function} [onReject]
208 * @return {MPromise} newPromise
209 */
210Promise.prototype.then = function (onFulfill, onReject) {
211 var newPromise = new Promise;
212
213 if ('function' == typeof onFulfill) {
214 this.onFulfill(handler(newPromise, onFulfill));
215 } else {
216 this.onFulfill(newPromise.fulfill.bind(newPromise));
217 }
218
219 if ('function' == typeof onReject) {
220 this.onReject(handler(newPromise, onReject));
221 } else {
222 this.onReject(newPromise.reject.bind(newPromise));
223 }
224
225 return newPromise;
226};
227
228
229function handler(promise, fn) {
230 function newTickHandler() {
231 var pDomain = promise.emitter.domain;
232 if (pDomain && pDomain !== process.domain) pDomain.enter();
233 try {
234 var x = fn.apply(undefined, boundHandler.args);
235 } catch (err) {
236 promise.reject(err);
237 return;
238 }
239 resolve(promise, x);
240 }
241 function boundHandler() {
242 boundHandler.args = arguments;
243 process.nextTick(newTickHandler);
244 }
245 return boundHandler;
246}
247
248
249function resolve(promise, x) {
250 function fulfillOnce() {
251 if (done++) return;
252 resolve.apply(undefined, strongUnshift(promise, arguments));
253 }
254 function rejectOnce(reason) {
255 if (done++) return;
256 promise.reject(reason);
257 }
258
259 if (promise === x) {
260 promise.reject(new TypeError("promise and x are the same"));
261 return;
262 }
263 var rest = toArray(arguments, 1);
264 var type = typeof x;
265 if ('undefined' == type || null == x || !('object' == type || 'function' == type)) {
266 promise.fulfill.apply(promise, rest);
267 return;
268 }
269
270 try {
271 var theThen = x.then;
272 } catch (err) {
273 promise.reject(err);
274 return;
275 }
276
277 if ('function' != typeof theThen) {
278 promise.fulfill.apply(promise, rest);
279 return;
280 }
281
282 var done = 0;
283 try {
284 var ret = theThen.call(x, fulfillOnce, rejectOnce);
285 return ret;
286 } catch (err) {
287 if (done++) return;
288 promise.reject(err);
289 }
290}
291
292
293/**
294 * Signifies that this promise was the last in a chain of `then()s`: if a handler passed to the call to `then` which produced this promise throws, the exception will go uncaught.
295 *
296 * ####Example:
297 *
298 * var p = new Promise;
299 * p.then(function(){ throw new Error('shucks') });
300 * setTimeout(function () {
301 * p.fulfill();
302 * // error was caught and swallowed by the promise returned from
303 * // p.then(). we either have to always register handlers on
304 * // the returned promises or we can do the following...
305 * }, 10);
306 *
307 * // this time we use .end() which prevents catching thrown errors
308 * var p = new Promise;
309 * var p2 = p.then(function(){ throw new Error('shucks') }).end(); // <--
310 * setTimeout(function () {
311 * p.fulfill(); // throws "shucks"
312 * }, 10);
313 *
314 * @api public
315 * @param {Function} [onReject]
316 * @return {MPromise} this
317 */
318Promise.prototype.end = Promise.prototype['catch'] = function (onReject) {
319 if (!onReject && !this.hasRejectListeners())
320 onReject = function idRejector(e) { throw e; };
321 this.onReject(onReject);
322 this.ended = true;
323 return this;
324};
325
326
327/**
328 * A debug utility function that adds handlers to a promise that will log some output to the `console`
329 *
330 * ####Example:
331 *
332 * var p = new Promise;
333 * p.then(function(){ throw new Error('shucks') });
334 * setTimeout(function () {
335 * p.fulfill();
336 * // error was caught and swallowed by the promise returned from
337 * // p.then(). we either have to always register handlers on
338 * // the returned promises or we can do the following...
339 * }, 10);
340 *
341 * // this time we use .end() which prevents catching thrown errors
342 * var p = new Promise;
343 * var p2 = p.then(function(){ throw new Error('shucks') }).end(); // <--
344 * setTimeout(function () {
345 * p.fulfill(); // throws "shucks"
346 * }, 10);
347 *
348 * @api public
349 * @param {MPromise} p
350 * @param {String} name
351 * @return {MPromise} this
352 */
353Promise.trace = function (p, name) {
354 p.then(
355 function () {
356 console.log("%s fulfill %j", name, toArray(arguments));
357 },
358 function () {
359 console.log("%s reject %j", name, toArray(arguments));
360 }
361 )
362};
363
364
365Promise.prototype.chain = function (p2) {
366 var p1 = this;
367 p1.onFulfill(p2.fulfill.bind(p2));
368 p1.onReject(p2.reject.bind(p2));
369 return p2;
370};
371
372
373Promise.prototype.all = function (promiseOfArr) {
374 var pRet = new Promise;
375 this.then(promiseOfArr).then(
376 function (promiseArr) {
377 var count = 0;
378 var ret = [];
379 var errSentinel;
380 if (!promiseArr.length) pRet.resolve();
381 promiseArr.forEach(function (promise, index) {
382 if (errSentinel) return;
383 count++;
384 promise.then(
385 function (val) {
386 if (errSentinel) return;
387 ret[index] = val;
388 --count;
389 if (count == 0) pRet.fulfill(ret);
390 },
391 function (err) {
392 if (errSentinel) return;
393 errSentinel = err;
394 pRet.reject(err);
395 }
396 );
397 });
398 return pRet;
399 }
400 , pRet.reject.bind(pRet)
401 );
402 return pRet;
403};
404
405
406Promise.hook = function (arr) {
407 var p1 = new Promise;
408 var pFinal = new Promise;
409 var signalP = function () {
410 --count;
411 if (count == 0)
412 pFinal.fulfill();
413 return pFinal;
414 };
415 var count = 1;
416 var ps = p1;
417 arr.forEach(function (hook) {
418 ps = ps.then(
419 function () {
420 var p = new Promise;
421 count++;
422 hook(p.resolve.bind(p), signalP);
423 return p;
424 }
425 )
426 });
427 ps = ps.then(signalP);
428 p1.resolve();
429 return ps;
430};
431
432
433/* This is for the A+ tests, but it's very useful as well */
434Promise.fulfilled = function fulfilled() { var p = new Promise; p.fulfill.apply(p, arguments); return p; };
435Promise.rejected = function rejected(reason) { return new Promise().reject(reason); };
436Promise.deferred = function deferred() {
437 var p = new Promise;
438 return {
439 promise: p,
440 reject: p.reject.bind(p),
441 resolve: p.fulfill.bind(p),
442 callback: p.resolve.bind(p)
443 }
444};
445/* End A+ tests adapter bit */