arnaucube
arnaucube/thoughtsPublic Self-hosted repository
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;