You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							860 lines
						
					
					
						
							22 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							860 lines
						
					
					
						
							22 KiB
						
					
					
				
								
							 | 
						|
								/**
							 | 
						|
								 * An object representing a "promise" for a future value
							 | 
						|
								 *
							 | 
						|
								 * @param {?function(T, ?)=} onSuccess a function to handle successful
							 | 
						|
								 *     resolution of this promise
							 | 
						|
								 * @param {?function(!Error, ?)=} onFail a function to handle failed
							 | 
						|
								 *     resolution of this promise
							 | 
						|
								 * @constructor
							 | 
						|
								 * @template T
							 | 
						|
								 */
							 | 
						|
								function Promise(onSuccess, onFail) {
							 | 
						|
								  this.promise = this
							 | 
						|
								  this._isPromise = true
							 | 
						|
								  this._successFn = onSuccess
							 | 
						|
								  this._failFn = onFail
							 | 
						|
								  this._scope = this
							 | 
						|
								  this._boundArgs = null
							 | 
						|
								  this._hasContext = false
							 | 
						|
								  this._nextContext = undefined
							 | 
						|
								  this._currentContext = undefined
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @param {function()} callback
							 | 
						|
								 */
							 | 
						|
								function nextTick (callback) {
							 | 
						|
								  callback()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								if (typeof process !== 'undefined' && typeof process.nextTick === 'function') {
							 | 
						|
								  nextTick = process.nextTick
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * All callback execution should go through this function.  While the
							 | 
						|
								 * implementation below is simple, it can be replaced with more sophisticated
							 | 
						|
								 * implementations that enforce QoS on the event loop.
							 | 
						|
								 *
							 | 
						|
								 * @param {Promise} defer
							 | 
						|
								 * @param {Function} callback
							 | 
						|
								 * @param {Object|undefined} scope
							 | 
						|
								 * @param {Array} args
							 | 
						|
								 */
							 | 
						|
								function nextTickCallback (defer, callback, scope, args) {
							 | 
						|
								  try {
							 | 
						|
								    defer.resolve(callback.apply(scope, args))
							 | 
						|
								  } catch (thrown) {
							 | 
						|
								    defer.reject(thrown)
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Used for accessing the nextTick function from outside the kew module.
							 | 
						|
								 *
							 | 
						|
								 * @return {Function}
							 | 
						|
								 */
							 | 
						|
								function getNextTickFunction () {
							 | 
						|
								  return nextTick
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Used for overriding the nextTick function from outside the kew module so that
							 | 
						|
								 * the user can plug and play lower level schedulers
							 | 
						|
								 * @param {!Function} fn
							 | 
						|
								 */
							 | 
						|
								function setNextTickFunction (fn) {
							 | 
						|
								  nextTick = fn
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Keep track of the number of promises that are rejected along side
							 | 
						|
								 * the number of rejected promises we call _failFn on so we can look
							 | 
						|
								 * for leaked rejections.
							 | 
						|
								 * @constructor
							 | 
						|
								 */
							 | 
						|
								function PromiseStats() {
							 | 
						|
								  /** @type {number} */
							 | 
						|
								  this.errorsEmitted = 0
							 | 
						|
								
							 | 
						|
								  /** @type {number} */
							 | 
						|
								  this.errorsHandled = 0
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								var stats = new PromiseStats()
							 | 
						|
								
							 | 
						|
								Promise.prototype._handleError = function () {
							 | 
						|
								  if (!this._errorHandled) {
							 | 
						|
								    stats.errorsHandled++
							 | 
						|
								    this._errorHandled = true
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Specify that the current promise should have a specified context
							 | 
						|
								 * @param  {*} context context
							 | 
						|
								 * @private
							 | 
						|
								 */
							 | 
						|
								Promise.prototype._useContext = function (context) {
							 | 
						|
								  this._nextContext = this._currentContext = context
							 | 
						|
								  this._hasContext = true
							 | 
						|
								  return this
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								Promise.prototype.clearContext = function () {
							 | 
						|
								  this._hasContext = false
							 | 
						|
								  this._nextContext = undefined
							 | 
						|
								  return this
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Set the context for all promise handlers to follow
							 | 
						|
								 *
							 | 
						|
								 * NOTE(dpup): This should be considered deprecated.  It does not do what most
							 | 
						|
								 * people would expect.  The context will be passed as a second argument to all
							 | 
						|
								 * subsequent callbacks.
							 | 
						|
								 *
							 | 
						|
								 * @param {*} context An arbitrary context
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.setContext = function (context) {
							 | 
						|
								  this._nextContext = context
							 | 
						|
								  this._hasContext = true
							 | 
						|
								  return this
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Get the context for a promise
							 | 
						|
								 * @return {*} the context set by setContext
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.getContext = function () {
							 | 
						|
								  return this._nextContext
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Resolve this promise with a specified value
							 | 
						|
								 *
							 | 
						|
								 * @param {*=} data
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.resolve = function (data) {
							 | 
						|
								  if (this._error || this._hasData) throw new Error("Unable to resolve or reject the same promise twice")
							 | 
						|
								
							 | 
						|
								  var i
							 | 
						|
								  if (data && isPromise(data)) {
							 | 
						|
								    this._child = data
							 | 
						|
								    if (this._promises) {
							 | 
						|
								      for (i = 0; i < this._promises.length; i += 1) {
							 | 
						|
								        data._chainPromise(this._promises[i])
							 | 
						|
								      }
							 | 
						|
								      delete this._promises
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    if (this._onComplete) {
							 | 
						|
								      for (i = 0; i < this._onComplete.length; i+= 1) {
							 | 
						|
								        data.fin(this._onComplete[i])
							 | 
						|
								      }
							 | 
						|
								      delete this._onComplete
							 | 
						|
								    }
							 | 
						|
								  } else if (data && isPromiseLike(data)) {
							 | 
						|
								    data.then(
							 | 
						|
								      function(data) { this.resolve(data) }.bind(this),
							 | 
						|
								      function(err) { this.reject(err) }.bind(this)
							 | 
						|
								    )
							 | 
						|
								  } else {
							 | 
						|
								    this._hasData = true
							 | 
						|
								    this._data = data
							 | 
						|
								
							 | 
						|
								    if (this._onComplete) {
							 | 
						|
								      for (i = 0; i < this._onComplete.length; i++) {
							 | 
						|
								        this._onComplete[i]()
							 | 
						|
								      }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    if (this._promises) {
							 | 
						|
								      for (i = 0; i < this._promises.length; i += 1) {
							 | 
						|
								        this._promises[i]._useContext(this._nextContext)
							 | 
						|
								        this._promises[i]._withInput(data)
							 | 
						|
								      }
							 | 
						|
								      delete this._promises
							 | 
						|
								    }
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Reject this promise with an error
							 | 
						|
								 *
							 | 
						|
								 * @param {!Error} e
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.reject = function (e) {
							 | 
						|
								  if (this._error || this._hasData) throw new Error("Unable to resolve or reject the same promise twice")
							 | 
						|
								
							 | 
						|
								  var i
							 | 
						|
								  this._error = e
							 | 
						|
								  stats.errorsEmitted++
							 | 
						|
								
							 | 
						|
								  if (this._ended) {
							 | 
						|
								    this._handleError()
							 | 
						|
								    process.nextTick(function onPromiseThrow() {
							 | 
						|
								      throw e
							 | 
						|
								    })
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if (this._onComplete) {
							 | 
						|
								    for (i = 0; i < this._onComplete.length; i++) {
							 | 
						|
								      this._onComplete[i]()
							 | 
						|
								    }
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if (this._promises) {
							 | 
						|
								    this._handleError()
							 | 
						|
								    for (i = 0; i < this._promises.length; i += 1) {
							 | 
						|
								      this._promises[i]._useContext(this._nextContext)
							 | 
						|
								      this._promises[i]._withError(e)
							 | 
						|
								    }
							 | 
						|
								    delete this._promises
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Provide a callback to be called whenever this promise successfully
							 | 
						|
								 * resolves. Allows for an optional second callback to handle the failure
							 | 
						|
								 * case.
							 | 
						|
								 *
							 | 
						|
								 * @param {?function(this:void, T, ?): RESULT|undefined} onSuccess
							 | 
						|
								 * @param {?function(this:void, !Error, ?): RESULT=} onFail
							 | 
						|
								 * @return {!Promise.<RESULT>} returns a new promise with the output of the onSuccess or
							 | 
						|
								 *     onFail handler
							 | 
						|
								 * @template RESULT
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.then = function (onSuccess, onFail) {
							 | 
						|
								  var promise = new Promise(onSuccess, onFail)
							 | 
						|
								  if (this._nextContext) promise._useContext(this._nextContext)
							 | 
						|
								
							 | 
						|
								  if (this._child) this._child._chainPromise(promise)
							 | 
						|
								  else this._chainPromise(promise)
							 | 
						|
								
							 | 
						|
								  return promise
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Provide a callback to be called whenever this promise successfully
							 | 
						|
								 * resolves. The callback will be executed in the context of the provided scope.
							 | 
						|
								 *
							 | 
						|
								 * @param {function(this:SCOPE, ...): RESULT} onSuccess
							 | 
						|
								 * @param {SCOPE} scope Object whose context callback will be executed in.
							 | 
						|
								 * @param {...*} var_args Additional arguments to be passed to the promise callback.
							 | 
						|
								 * @return {!Promise.<RESULT>} returns a new promise with the output of the onSuccess
							 | 
						|
								 * @template SCOPE, RESULT
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.thenBound = function (onSuccess, scope, var_args) {
							 | 
						|
								  var promise = new Promise(onSuccess)
							 | 
						|
								  if (this._nextContext) promise._useContext(this._nextContext)
							 | 
						|
								
							 | 
						|
								  promise._scope = scope
							 | 
						|
								  if (arguments.length > 2) {
							 | 
						|
								    promise._boundArgs = Array.prototype.slice.call(arguments, 2)
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  // Chaining must happen after setting args and scope since it may fire callback.
							 | 
						|
								  if (this._child) this._child._chainPromise(promise)
							 | 
						|
								  else this._chainPromise(promise)
							 | 
						|
								
							 | 
						|
								  return promise
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Provide a callback to be called whenever this promise is rejected
							 | 
						|
								 *
							 | 
						|
								 * @param {function(this:void, !Error, ?)} onFail
							 | 
						|
								 * @return {!Promise.<T>} returns a new promise with the output of the onFail handler
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.fail = function (onFail) {
							 | 
						|
								  return this.then(null, onFail)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Provide a callback to be called whenever this promise is rejected.
							 | 
						|
								 * The callback will be executed in the context of the provided scope.
							 | 
						|
								 *
							 | 
						|
								 * @param {function(this:SCOPE, ...)} onFail
							 | 
						|
								 * @param {SCOPE} scope Object whose context callback will be executed in.
							 | 
						|
								 * @param {...?} var_args
							 | 
						|
								 * @return {!Promise.<T>} returns a new promise with the output of the onSuccess
							 | 
						|
								 * @template SCOPE
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.failBound = function (onFail, scope, var_args) {
							 | 
						|
								  var promise = new Promise(null, onFail)
							 | 
						|
								  if (this._nextContext) promise._useContext(this._nextContext)
							 | 
						|
								
							 | 
						|
								  promise._scope = scope
							 | 
						|
								  if (arguments.length > 2) {
							 | 
						|
								    promise._boundArgs = Array.prototype.slice.call(arguments, 2)
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  // Chaining must happen after setting args and scope since it may fire callback.
							 | 
						|
								  if (this._child) this._child._chainPromise(promise)
							 | 
						|
								  else this._chainPromise(promise)
							 | 
						|
								
							 | 
						|
								  return promise
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Spread a promises outputs to the functions arguments.
							 | 
						|
								 * @param {?function(this:void, ...): RESULT|undefined} onSuccess
							 | 
						|
								 * @return {!Promise.<RESULT>} returns a new promise with the output of the onSuccess or
							 | 
						|
								 *     onFail handler
							 | 
						|
								 * @template RESULT
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.spread = function (onSuccess) {
							 | 
						|
								  return this.then(allInternal)
							 | 
						|
								  .then(function (array) {
							 | 
						|
								    return onSuccess.apply(null, array)
							 | 
						|
								  })
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Spread a promises outputs to the functions arguments.
							 | 
						|
								 * @param {function(this:SCOPE, ...): RESULT} onSuccess
							 | 
						|
								 * @param {SCOPE} scope Object whose context callback will be executed in.
							 | 
						|
								 * @param {...*} var_args Additional arguments to be passed to the promise callback.
							 | 
						|
								 * @return {!Promise.<RESULT>} returns a new promise with the output of the onSuccess
							 | 
						|
								 * @template SCOPE, RESULT
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.spreadBound = function (onSuccess, scope, var_args) {
							 | 
						|
								  var args = Array.prototype.slice.call(arguments, 2)
							 | 
						|
								  return this.then(allInternal)
							 | 
						|
								  .then(function (array) {
							 | 
						|
								    return onSuccess.apply(scope, args.concat(array))
							 | 
						|
								  })
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Provide a callback to be called whenever this promise is either resolved
							 | 
						|
								 * or rejected.
							 | 
						|
								 *
							 | 
						|
								 * @param {function()} onComplete
							 | 
						|
								 * @return {!Promise.<T>} returns the current promise
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.fin = function (onComplete) {
							 | 
						|
								  if (this._hasData || this._error) {
							 | 
						|
								    onComplete()
							 | 
						|
								    return this
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if (this._child) {
							 | 
						|
								    this._child.fin(onComplete)
							 | 
						|
								  } else {
							 | 
						|
								    if (!this._onComplete) this._onComplete = [onComplete]
							 | 
						|
								    else this._onComplete.push(onComplete)
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  return this
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Mark this promise as "ended". If the promise is rejected, this will throw an
							 | 
						|
								 * error in whatever scope it happens to be in
							 | 
						|
								 *
							 | 
						|
								 * @return {!Promise.<T>} returns the current promise
							 | 
						|
								 * @deprecated Prefer done(), because it's consistent with Q.
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.end = function () {
							 | 
						|
								  this._end()
							 | 
						|
								  return this
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Mark this promise as "ended".
							 | 
						|
								 * @private
							 | 
						|
								 */
							 | 
						|
								Promise.prototype._end = function () {
							 | 
						|
								  if (this._error) {
							 | 
						|
								    this._handleError()
							 | 
						|
								    throw this._error
							 | 
						|
								  }
							 | 
						|
								  this._ended = true
							 | 
						|
								  return this
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Close the promise. Any errors after this completes will be thrown to the global handler.
							 | 
						|
								 *
							 | 
						|
								 * @param {?function(this:void, T, ?)=} onSuccess a function to handle successful
							 | 
						|
								 *     resolution of this promise
							 | 
						|
								 * @param {?function(this:void, !Error, ?)=} onFailure a function to handle failed
							 | 
						|
								 *     resolution of this promise
							 | 
						|
								 * @return {void}
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.done = function (onSuccess, onFailure) {
							 | 
						|
								  var self = this
							 | 
						|
								  if (onSuccess || onFailure) {
							 | 
						|
								    self = self.then(onSuccess, onFailure)
							 | 
						|
								  }
							 | 
						|
								  self._end()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Return a new promise that behaves the same as the current promise except
							 | 
						|
								 * that it will be rejected if the current promise does not get fulfilled
							 | 
						|
								 * after a certain amount of time.
							 | 
						|
								 *
							 | 
						|
								 * @param {number} timeoutMs The timeout threshold in msec
							 | 
						|
								 * @param {string=} timeoutMsg error message
							 | 
						|
								 * @return {!Promise.<T>} a new promise with timeout
							 | 
						|
								 */
							 | 
						|
								 Promise.prototype.timeout = function (timeoutMs, timeoutMsg) {
							 | 
						|
								  var deferred = new Promise()
							 | 
						|
								  var isTimeout = false
							 | 
						|
								
							 | 
						|
								  var timeout = setTimeout(function() {
							 | 
						|
								    deferred.reject(new Error(timeoutMsg || 'Promise timeout after ' + timeoutMs + ' ms.'))
							 | 
						|
								    isTimeout = true
							 | 
						|
								  }, timeoutMs)
							 | 
						|
								
							 | 
						|
								  this.then(function (data) {
							 | 
						|
								    if (!isTimeout) {
							 | 
						|
								      clearTimeout(timeout)
							 | 
						|
								      deferred.resolve(data)
							 | 
						|
								    }
							 | 
						|
								  },
							 | 
						|
								  function (err) {
							 | 
						|
								    if (!isTimeout) {
							 | 
						|
								      clearTimeout(timeout)
							 | 
						|
								      deferred.reject(err)
							 | 
						|
								    }
							 | 
						|
								  })
							 | 
						|
								
							 | 
						|
								  return deferred.promise
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Attempt to resolve this promise with the specified input
							 | 
						|
								 *
							 | 
						|
								 * @param {*} data the input
							 | 
						|
								 */
							 | 
						|
								Promise.prototype._withInput = function (data) {
							 | 
						|
								  if (this._successFn) {
							 | 
						|
								    this._nextTick(this._successFn, [data, this._currentContext])
							 | 
						|
								  } else {
							 | 
						|
								    this.resolve(data)
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  // context is no longer needed
							 | 
						|
								  delete this._currentContext
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Attempt to reject this promise with the specified error
							 | 
						|
								 *
							 | 
						|
								 * @param {!Error} e
							 | 
						|
								 * @private
							 | 
						|
								 */
							 | 
						|
								Promise.prototype._withError = function (e) {
							 | 
						|
								  if (this._failFn) {
							 | 
						|
								    this._nextTick(this._failFn, [e, this._currentContext])
							 | 
						|
								  } else {
							 | 
						|
								    this.reject(e)
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  // context is no longer needed
							 | 
						|
								  delete this._currentContext
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Calls a function in the correct scope, and includes bound arguments.
							 | 
						|
								 * @param {Function} fn
							 | 
						|
								 * @param {Array} args
							 | 
						|
								 * @private
							 | 
						|
								 */
							 | 
						|
								Promise.prototype._nextTick = function (fn, args) {
							 | 
						|
								  if (this._boundArgs) {
							 | 
						|
								    args = this._boundArgs.concat(args)
							 | 
						|
								  }
							 | 
						|
								  nextTick(nextTickCallback.bind(null, this, fn, this._scope, args))
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Chain a promise to the current promise
							 | 
						|
								 *
							 | 
						|
								 * @param {!Promise} promise the promise to chain
							 | 
						|
								 * @private
							 | 
						|
								 */
							 | 
						|
								Promise.prototype._chainPromise = function (promise) {
							 | 
						|
								  var i
							 | 
						|
								  if (this._hasContext) promise._useContext(this._nextContext)
							 | 
						|
								
							 | 
						|
								  if (this._child) {
							 | 
						|
								    this._child._chainPromise(promise)
							 | 
						|
								  } else if (this._hasData) {
							 | 
						|
								    promise._withInput(this._data)
							 | 
						|
								  } else if (this._error) {
							 | 
						|
								    // We can't rely on _withError() because it's called on the chained promises
							 | 
						|
								    // and we need to use the source's _errorHandled state
							 | 
						|
								    this._handleError()
							 | 
						|
								    promise._withError(this._error)
							 | 
						|
								  } else if (!this._promises) {
							 | 
						|
								    this._promises = [promise]
							 | 
						|
								  } else {
							 | 
						|
								    this._promises.push(promise)
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Utility function used for creating a node-style resolver
							 | 
						|
								 * for deferreds
							 | 
						|
								 *
							 | 
						|
								 * @param {!Promise} deferred a promise that looks like a deferred
							 | 
						|
								 * @param {Error=} err an optional error
							 | 
						|
								 * @param {*=} data optional data
							 | 
						|
								 */
							 | 
						|
								function resolver(deferred, err, data) {
							 | 
						|
								  if (err) deferred.reject(err)
							 | 
						|
								  else deferred.resolve(data)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Creates a node-style resolver for a deferred by wrapping
							 | 
						|
								 * resolver()
							 | 
						|
								 *
							 | 
						|
								 * @return {function(?Error, *)} node-style callback
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.makeNodeResolver = function () {
							 | 
						|
								  return resolver.bind(null, this)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Return true iff the given object is a promise of this library.
							 | 
						|
								 *
							 | 
						|
								 * Because kew's API is slightly different than other promise libraries,
							 | 
						|
								 * it's important that we have a test for its promise type. If you want
							 | 
						|
								 * to test for a more general A+ promise, you should do a cap test for
							 | 
						|
								 * the features you want.
							 | 
						|
								 *
							 | 
						|
								 * @param {*} obj The object to test
							 | 
						|
								 * @return {boolean} Whether the object is a promise
							 | 
						|
								 */
							 | 
						|
								function isPromise(obj) {
							 | 
						|
								  return !!obj._isPromise
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Return true iff the given object is a promise-like object, e.g. appears to
							 | 
						|
								 * implement Promises/A+ specification
							 | 
						|
								 *
							 | 
						|
								 * @param {*} obj The object to test
							 | 
						|
								 * @return {boolean} Whether the object is a promise-like object
							 | 
						|
								 */
							 | 
						|
								function isPromiseLike(obj) {
							 | 
						|
								  return (typeof obj === 'object' || typeof obj === 'function') &&
							 | 
						|
								    typeof obj.then === 'function'
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Static function which creates and resolves a promise immediately
							 | 
						|
								 *
							 | 
						|
								 * @param {T} data data to resolve the promise with
							 | 
						|
								 * @return {!Promise.<T>}
							 | 
						|
								 * @template T
							 | 
						|
								 */
							 | 
						|
								function resolve(data) {
							 | 
						|
								  var promise = new Promise()
							 | 
						|
								  promise.resolve(data)
							 | 
						|
								  return promise
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Static function which creates and rejects a promise immediately
							 | 
						|
								 *
							 | 
						|
								 * @param {!Error} e error to reject the promise with
							 | 
						|
								 * @return {!Promise}
							 | 
						|
								 */
							 | 
						|
								function reject(e) {
							 | 
						|
								  var promise = new Promise()
							 | 
						|
								  promise.reject(e)
							 | 
						|
								  return promise
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Replace an element in an array with a new value. Used by .all() to
							 | 
						|
								 * call from .then()
							 | 
						|
								 *
							 | 
						|
								 * @param {!Array} arr
							 | 
						|
								 * @param {number} idx
							 | 
						|
								 * @param {*} val
							 | 
						|
								 * @return {*} the val that's being injected into the array
							 | 
						|
								 */
							 | 
						|
								function replaceEl(arr, idx, val) {
							 | 
						|
								  arr[idx] = val
							 | 
						|
								  return val
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Replace an element in an array as it is resolved with its value.
							 | 
						|
								 * Used by .allSettled().
							 | 
						|
								 *
							 | 
						|
								 * @param {!Array} arr
							 | 
						|
								 * @param {number} idx
							 | 
						|
								 * @param {*} value The value from a resolved promise.
							 | 
						|
								 * @return {*} the data that's being passed in
							 | 
						|
								 */
							 | 
						|
								function replaceElFulfilled(arr, idx, value) {
							 | 
						|
								  arr[idx] = {
							 | 
						|
								    state: 'fulfilled',
							 | 
						|
								    value: value
							 | 
						|
								  }
							 | 
						|
								  return value
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Replace an element in an array as it is rejected with the reason.
							 | 
						|
								 * Used by .allSettled().
							 | 
						|
								 *
							 | 
						|
								 * @param {!Array} arr
							 | 
						|
								 * @param {number} idx
							 | 
						|
								 * @param {*} reason The reason why the original promise is rejected
							 | 
						|
								 * @return {*} the data that's being passed in
							 | 
						|
								 */
							 | 
						|
								function replaceElRejected(arr, idx, reason) {
							 | 
						|
								  arr[idx] = {
							 | 
						|
								    state: 'rejected',
							 | 
						|
								    reason: reason
							 | 
						|
								  }
							 | 
						|
								  return reason
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Takes in an array of promises or literals and returns a promise which returns
							 | 
						|
								 * an array of values when all have resolved. If any fail, the promise fails.
							 | 
						|
								 *
							 | 
						|
								 * @param {!Array.<!Promise>} promises
							 | 
						|
								 * @return {!Promise.<!Array>}
							 | 
						|
								 */
							 | 
						|
								function all(promises) {
							 | 
						|
								  if (arguments.length != 1 || !Array.isArray(promises)) {
							 | 
						|
								    promises = Array.prototype.slice.call(arguments, 0)
							 | 
						|
								  }
							 | 
						|
								  return allInternal(promises)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * A version of all() that does not accept var_args
							 | 
						|
								 *
							 | 
						|
								 * @param {!Array.<!Promise>} promises
							 | 
						|
								 * @return {!Promise.<!Array>}
							 | 
						|
								 */
							 | 
						|
								function allInternal(promises) {
							 | 
						|
								  if (!promises.length) return resolve([])
							 | 
						|
								
							 | 
						|
								  var outputs = []
							 | 
						|
								  var finished = false
							 | 
						|
								  var promise = new Promise()
							 | 
						|
								  var counter = promises.length
							 | 
						|
								
							 | 
						|
								  for (var i = 0; i < promises.length; i += 1) {
							 | 
						|
								    if (!promises[i] || !isPromiseLike(promises[i])) {
							 | 
						|
								      outputs[i] = promises[i]
							 | 
						|
								      counter -= 1
							 | 
						|
								    } else {
							 | 
						|
								      promises[i].then(replaceEl.bind(null, outputs, i))
							 | 
						|
								      .then(function decrementAllCounter() {
							 | 
						|
								        counter--
							 | 
						|
								        if (!finished && counter === 0) {
							 | 
						|
								          finished = true
							 | 
						|
								          promise.resolve(outputs)
							 | 
						|
								        }
							 | 
						|
								      }, function onAllError(e) {
							 | 
						|
								        if (!finished) {
							 | 
						|
								          finished = true
							 | 
						|
								          promise.reject(e)
							 | 
						|
								        }
							 | 
						|
								      })
							 | 
						|
								    }
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if (counter === 0 && !finished) {
							 | 
						|
								    finished = true
							 | 
						|
								    promise.resolve(outputs)
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  return promise
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Takes in an array of promises or values and returns a promise that is
							 | 
						|
								 * fulfilled with an array of state objects when all have resolved or
							 | 
						|
								 * rejected. If a promise is resolved, its corresponding state object is
							 | 
						|
								 * {state: 'fulfilled', value: Object}; whereas if a promise is rejected, its
							 | 
						|
								 * corresponding state object is {state: 'rejected', reason: Object}.
							 | 
						|
								 *
							 | 
						|
								 * @param {!Array} promises or values
							 | 
						|
								 * @return {!Promise.<!Array>} Promise fulfilled with state objects for each input
							 | 
						|
								 */
							 | 
						|
								function allSettled(promises) {
							 | 
						|
								  if (!Array.isArray(promises)) {
							 | 
						|
								    throw Error('The input to "allSettled()" should be an array of Promise or values')
							 | 
						|
								  }
							 | 
						|
								  if (!promises.length) return resolve([])
							 | 
						|
								
							 | 
						|
								  var outputs = []
							 | 
						|
								  var promise = new Promise()
							 | 
						|
								  var counter = promises.length
							 | 
						|
								
							 | 
						|
								  for (var i = 0; i < promises.length; i += 1) {
							 | 
						|
								    if (!promises[i] || !isPromiseLike(promises[i])) {
							 | 
						|
								      replaceElFulfilled(outputs, i, promises[i])
							 | 
						|
								      if ((--counter) === 0) promise.resolve(outputs)
							 | 
						|
								    } else {
							 | 
						|
								      promises[i]
							 | 
						|
								        .then(replaceElFulfilled.bind(null, outputs, i), replaceElRejected.bind(null, outputs, i))
							 | 
						|
								        .then(function () {
							 | 
						|
								          if ((--counter) === 0) promise.resolve(outputs)
							 | 
						|
								        })
							 | 
						|
								    }
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  return promise
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Takes an array of results and spreads them to the arguments of a function.
							 | 
						|
								 * @param {!Array} array
							 | 
						|
								 * @param {!Function} fn
							 | 
						|
								 */
							 | 
						|
								function spread(array, fn) {
							 | 
						|
								  resolve(array).spread(fn)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Create a new Promise which looks like a deferred
							 | 
						|
								 *
							 | 
						|
								 * @return {!Promise}
							 | 
						|
								 */
							 | 
						|
								function defer() {
							 | 
						|
								  return new Promise()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Return a promise which will wait a specified number of ms to resolve
							 | 
						|
								 *
							 | 
						|
								 * @param {*} delayMsOrVal A delay (in ms) if this takes one argument, or ther
							 | 
						|
								 *     return value if it takes two.
							 | 
						|
								 * @param {number=} opt_delayMs
							 | 
						|
								 * @return {!Promise}
							 | 
						|
								 */
							 | 
						|
								function delay(delayMsOrVal, opt_delayMs) {
							 | 
						|
								  var returnVal = undefined
							 | 
						|
								  var delayMs = delayMsOrVal
							 | 
						|
								  if (typeof opt_delayMs != 'undefined') {
							 | 
						|
								    delayMs = opt_delayMs
							 | 
						|
								    returnVal = delayMsOrVal
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  if (typeof delayMs != 'number') {
							 | 
						|
								    throw new Error('Bad delay value ' + delayMs)
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  var defer = new Promise()
							 | 
						|
								  setTimeout(function onDelay() {
							 | 
						|
								    defer.resolve(returnVal)
							 | 
						|
								  }, delayMs)
							 | 
						|
								  return defer
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Returns a promise that has the same result as `this`, but fulfilled
							 | 
						|
								 * after at least ms milliseconds
							 | 
						|
								 * @param {number} ms
							 | 
						|
								 */
							 | 
						|
								Promise.prototype.delay = function (ms) {
							 | 
						|
								  return this.then(function (val) {
							 | 
						|
								    return delay(val, ms)
							 | 
						|
								  })
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Return a promise which will evaluate the function fn in a future turn with
							 | 
						|
								 * the provided args
							 | 
						|
								 *
							 | 
						|
								 * @param {function(...)} fn
							 | 
						|
								 * @param {...*} var_args a variable number of arguments
							 | 
						|
								 * @return {!Promise}
							 | 
						|
								 */
							 | 
						|
								function fcall(fn, var_args) {
							 | 
						|
								  var rootArgs = Array.prototype.slice.call(arguments, 1)
							 | 
						|
								  var defer = new Promise()
							 | 
						|
								  nextTick(nextTickCallback.bind(null, defer, fn, undefined, rootArgs))
							 | 
						|
								  return defer
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Returns a promise that will be invoked with the result of a node style
							 | 
						|
								 * callback. All args to fn should be given except for the final callback arg
							 | 
						|
								 *
							 | 
						|
								 * @param {function(...)} fn
							 | 
						|
								 * @param {...*} var_args a variable number of arguments
							 | 
						|
								 * @return {!Promise}
							 | 
						|
								 */
							 | 
						|
								function nfcall(fn, var_args) {
							 | 
						|
								  // Insert an undefined argument for scope and let bindPromise() do the work.
							 | 
						|
								  var args = Array.prototype.slice.call(arguments, 0)
							 | 
						|
								  args.splice(1, 0, undefined)
							 | 
						|
								  return ncall.apply(undefined, args)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Like `nfcall`, but permits passing a `this` context for the call.
							 | 
						|
								 *
							 | 
						|
								 * @param {function(...)} fn
							 | 
						|
								 * @param {Object} scope
							 | 
						|
								 * @param {...*} var_args
							 | 
						|
								 * @return {!Promise}
							 | 
						|
								 */
							 | 
						|
								function ncall(fn, scope, var_args) {
							 | 
						|
								  return bindPromise.apply(null, arguments)()
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Binds a function to a scope with an optional number of curried arguments. Attaches
							 | 
						|
								 * a node style callback as the last argument and returns a promise
							 | 
						|
								 *
							 | 
						|
								 * @param {function(...)} fn
							 | 
						|
								 * @param {Object} scope
							 | 
						|
								 * @param {...*} var_args a variable number of arguments
							 | 
						|
								 * @return {function(...)}: !Promise}
							 | 
						|
								 */
							 | 
						|
								function bindPromise(fn, scope, var_args) {
							 | 
						|
								  var rootArgs = Array.prototype.slice.call(arguments, 2)
							 | 
						|
								  return function onBoundPromise(var_args) {
							 | 
						|
								    var defer = new Promise()
							 | 
						|
								    try {
							 | 
						|
								      fn.apply(scope, rootArgs.concat(Array.prototype.slice.call(arguments, 0), defer.makeNodeResolver()))
							 | 
						|
								    } catch (e) {
							 | 
						|
								      defer.reject(e)
							 | 
						|
								    }
							 | 
						|
								    return defer
							 | 
						|
								  }
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								module.exports = {
							 | 
						|
								  all: all,
							 | 
						|
								  bindPromise: bindPromise,
							 | 
						|
								  defer: defer,
							 | 
						|
								  delay: delay,
							 | 
						|
								  fcall: fcall,
							 | 
						|
								  isPromise: isPromise,
							 | 
						|
								  isPromiseLike: isPromiseLike,
							 | 
						|
								  ncall: ncall,
							 | 
						|
								  nfcall: nfcall,
							 | 
						|
								  resolve: resolve,
							 | 
						|
								  reject: reject,
							 | 
						|
								  spread: spread,
							 | 
						|
								  stats: stats,
							 | 
						|
								  allSettled: allSettled,
							 | 
						|
								  Promise: Promise,
							 | 
						|
								  getNextTickFunction: getNextTickFunction,
							 | 
						|
								  setNextTickFunction: setNextTickFunction,
							 | 
						|
								}
							 | 
						|
								
							 |