thoughts/node_modules/mongoose/node_modules/mpromise/lib/promise.js
node_modules/mongoose/node_modules/mpromise/lib/promise.jsRaw
1
2/*!
3 * Module dependencies.
4 */
5
6var slice = require('sliced');
7var EventEmitter = require('events').EventEmitter;
8
9/**
10 * Promise constructor.
11 *
12 * _NOTE: The success and failure event names can be overridden by setting `Promise.SUCCESS` and `Promise.FAILURE` respectively._
13 *
14 * @param {Function} back a function that accepts `fn(err, ...){}` as signature
15 * @inherits NodeJS EventEmitter http://nodejs.org/api/events.html#events_class_events_eventemitter
16 * @event `reject`: Emits when the promise is rejected (event name may be overridden)
17 * @event `fulfill`: Emits when the promise is fulfilled (event name may be overridden)
18 * @api public
19 */
20
21function Promise (back) {
22 this.emitted = {};
23 this.ended = false;
24 if ('function' == typeof back)
25 this.onResolve(back);
26}
27
28/*!
29 * event names
30 */
31
32Promise.SUCCESS = 'fulfill';
33Promise.FAILURE = 'reject';
34
35/*!
36 * Inherits from EventEmitter.
37 */
38
39Promise.prototype.__proto__ = EventEmitter.prototype;
40
41/**
42 * Adds `listener` to the `event`.
43 *
44 * 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.
45 *
46 * @param {String} event
47 * @param {Function} callback
48 * @return {Promise} this
49 * @api public
50 */
51
52Promise.prototype.on = function (event, callback) {
53 if (this.emitted[event])
54 callback.apply(this, this.emitted[event]);
55 else
56 EventEmitter.prototype.on.call(this, event, callback);
57
58 return this;
59}
60
61/**
62 * Keeps track of emitted events to run them on `on`.
63 *
64 * @api private
65 */
66
67Promise.prototype.emit = function (event) {
68 // ensures a promise can't be fulfill() or reject() more than once
69 var success = this.constructor.SUCCESS;
70 var failure = this.constructor.FAILURE;
71
72 if (event == success || event == failure) {
73 if (this.emitted[success] || this.emitted[failure]) {
74 return this;
75 }
76 this.emitted[event] = slice(arguments, 1);
77 }
78
79 return EventEmitter.prototype.emit.apply(this, arguments);
80}
81
82/**
83 * Fulfills this promise with passed arguments.
84 *
85 * If this promise has already been fulfilled or rejected, no action is taken.
86 *
87 * @api public
88 */
89
90Promise.prototype.fulfill = function () {
91 var args = slice(arguments);
92 return this.emit.apply(this, [this.constructor.SUCCESS].concat(args));
93}
94
95/**
96 * Rejects this promise with `reason`.
97 *
98 * If this promise has already been fulfilled or rejected, no action is taken.
99 *
100 * @api public
101 * @param {Object|String} reason
102 * @return {Promise} this
103 */
104
105Promise.prototype.reject = function (reason) {
106 return this.emit(this.constructor.FAILURE, reason);
107}
108
109/**
110 * Resolves this promise to a rejected state if `err` is passed or
111 * fulfilled state if no `err` is passed.
112 *
113 * @param {Error} [err] error or null
114 * @param {Object} [val] value to fulfill the promise with
115 * @api public
116 */
117
118Promise.prototype.resolve = function (err, val) {
119 if (err) return this.reject(err);
120 return this.fulfill(val);
121}
122
123/**
124 * Adds a listener to the SUCCESS event.
125 *
126 * @return {Promise} this
127 * @api public
128 */
129
130Promise.prototype.onFulfill = function (fn) {
131 return this.on(this.constructor.SUCCESS, fn);
132}
133
134/**
135 * Adds a listener to the FAILURE event.
136 *
137 * @return {Promise} this
138 * @api public
139 */
140
141Promise.prototype.onReject = function (fn) {
142 return this.on(this.constructor.FAILURE, fn);
143}
144
145/**
146 * Adds a single function as a listener to both SUCCESS and FAILURE.
147 *
148 * It will be executed with traditional node.js argument position:
149 * function (err, args...) {}
150 *
151 * @param {Function} fn
152 * @return {Promise} this
153 */
154
155Promise.prototype.onResolve = function (fn) {
156 this.on(this.constructor.FAILURE, function(err){
157 fn.call(this, err);
158 });
159
160 this.on(this.constructor.SUCCESS, function(){
161 var args = slice(arguments);
162 fn.apply(this, [null].concat(args));
163 });
164
165 return this;
166}
167
168/**
169 * Creates a new promise and returns it. If `onFulfill` or
170 * `onReject` are passed, they are added as SUCCESS/ERROR callbacks
171 * to this promise after the nextTick.
172 *
173 * Conforms to [promises/A+](https://github.com/promises-aplus/promises-spec) specification. Read for more detail how to use this method.
174 *
175 * ####Example:
176 *
177 * var p = new Promise;
178 * p.then(function (arg) {
179 * return arg + 1;
180 * }).then(function (arg) {
181 * throw new Error(arg + ' is an error!');
182 * }).then(null, function (err) {
183 * assert.ok(err instanceof Error);
184 * assert.equal('2 is an error', err.message);
185 * });
186 * p.complete(1);
187 *
188 * @see promises-A+ https://github.com/promises-aplus/promises-spec
189 * @param {Function} onFulFill
190 * @param {Function} onReject
191 * @return {Promise} newPromise
192 */
193
194Promise.prototype.then = function (onFulfill, onReject) {
195 var self = this
196 , retPromise = new Promise;
197
198 function handler (fn) {
199 return function handle (arg) {
200 var val;
201
202 try {
203 val = fn(arg);
204 } catch (err) {
205 if (retPromise.ended) throw err;
206 return retPromise.reject(err);
207 }
208
209 if (val && 'function' == typeof val.then) {
210 val.then(
211 retPromise.fulfill.bind(retPromise)
212 , retPromise.reject.bind(retPromise))
213 } else {
214 retPromise.fulfill(val);
215 }
216 }
217 }
218
219 process.nextTick(function () {
220 if ('function' == typeof onReject) {
221 self.onReject(handler(onReject));
222 } else {
223 self.onReject(retPromise.reject.bind(retPromise));
224 }
225
226 if ('function' == typeof onFulfill) {
227 self.onFulfill(handler(onFulfill));
228 } else {
229 self.onFulfill(retPromise.fulfill.bind(retPromise));
230 }
231 })
232
233 return retPromise;
234}
235
236/**
237 * 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.
238 *
239 * ####Example:
240 *
241 * var p = new Promise;
242 * p.then(function(){ throw new Error('shucks') });
243 * setTimeout(function () {
244 * p.fulfill();
245 * // error was caught and swallowed by the promise returned from
246 * // p.then(). we either have to always register handlers on
247 * // the returned promises or we can do the following...
248 * }, 10);
249 *
250 * // this time we use .end() which prevents catching thrown errors
251 * var p = new Promise;
252 * var p2 = p.then(function(){ throw new Error('shucks') }).end(); // <--
253 * setTimeout(function () {
254 * p.fulfill(); // throws "shucks"
255 * }, 10);
256 *
257 * @api public
258 */
259
260Promise.prototype.end = function () {
261 this.ended = true;
262}
263
264/*!
265 * Module exports.
266 */
267
268module.exports = Promise;