| "use strict"; |
| module.exports = function(Promise, |
| PromiseArray, |
| apiRejection, |
| tryConvertToPromise, |
| INTERNAL) { |
| var async = require("./async.js"); |
| var util = require("./util.js"); |
| var tryCatch = util.tryCatch; |
| var errorObj = util.errorObj; |
| function ReductionPromiseArray(promises, fn, accum, _each) { |
| this.constructor$(promises); |
| this._promise._captureStackTrace(); |
| this._preservedValues = _each === INTERNAL ? [] : null; |
| this._zerothIsAccum = (accum === undefined); |
| this._gotAccum = false; |
| this._reducingIndex = (this._zerothIsAccum ? 1 : 0); |
| this._valuesPhase = undefined; |
| var maybePromise = tryConvertToPromise(accum, this._promise); |
| var rejected = false; |
| var isPromise = maybePromise instanceof Promise; |
| if (isPromise) { |
| maybePromise = maybePromise._target(); |
| if (maybePromise._isPending()) { |
| maybePromise._proxyPromiseArray(this, -1); |
| } else if (maybePromise._isFulfilled()) { |
| accum = maybePromise._value(); |
| this._gotAccum = true; |
| } else { |
| this._reject(maybePromise._reason()); |
| rejected = true; |
| } |
| } |
| if (!(isPromise || this._zerothIsAccum)) this._gotAccum = true; |
| this._callback = fn; |
| this._accum = accum; |
| if (!rejected) async.invoke(init, this, undefined); |
| } |
| function init() { |
| this._init$(undefined, -5); |
| } |
| util.inherits(ReductionPromiseArray, PromiseArray); |
| |
| ReductionPromiseArray.prototype._init = function () {}; |
| |
| ReductionPromiseArray.prototype._resolveEmptyArray = function () { |
| if (this._gotAccum || this._zerothIsAccum) { |
| this._resolve(this._preservedValues !== null |
| ? [] : this._accum); |
| } |
| }; |
| |
| ReductionPromiseArray.prototype._promiseFulfilled = function (value, index) { |
| var values = this._values; |
| values[index] = value; |
| var length = this.length(); |
| var preservedValues = this._preservedValues; |
| var isEach = preservedValues !== null; |
| var gotAccum = this._gotAccum; |
| var valuesPhase = this._valuesPhase; |
| var valuesPhaseIndex; |
| if (!valuesPhase) { |
| valuesPhase = this._valuesPhase = new Array(length); |
| for (valuesPhaseIndex=0; valuesPhaseIndex<length; ++valuesPhaseIndex) { |
| valuesPhase[valuesPhaseIndex] = 0; |
| } |
| } |
| valuesPhaseIndex = valuesPhase[index]; |
| |
| if (index === 0 && this._zerothIsAccum) { |
| this._accum = value; |
| this._gotAccum = gotAccum = true; |
| valuesPhase[index] = ((valuesPhaseIndex === 0) |
| ? 1 : 2); |
| } else if (index === -1) { |
| this._accum = value; |
| this._gotAccum = gotAccum = true; |
| } else { |
| if (valuesPhaseIndex === 0) { |
| valuesPhase[index] = 1; |
| } else { |
| valuesPhase[index] = 2; |
| this._accum = value; |
| } |
| } |
| if (!gotAccum) return; |
| |
| var callback = this._callback; |
| var receiver = this._promise._boundTo; |
| var ret; |
| |
| for (var i = this._reducingIndex; i < length; ++i) { |
| valuesPhaseIndex = valuesPhase[i]; |
| if (valuesPhaseIndex === 2) { |
| this._reducingIndex = i + 1; |
| continue; |
| } |
| if (valuesPhaseIndex !== 1) return; |
| value = values[i]; |
| this._promise._pushContext(); |
| if (isEach) { |
| preservedValues.push(value); |
| ret = tryCatch(callback).call(receiver, value, i, length); |
| } |
| else { |
| ret = tryCatch(callback) |
| .call(receiver, this._accum, value, i, length); |
| } |
| this._promise._popContext(); |
| |
| if (ret === errorObj) return this._reject(ret.e); |
| |
| var maybePromise = tryConvertToPromise(ret, this._promise); |
| if (maybePromise instanceof Promise) { |
| maybePromise = maybePromise._target(); |
| if (maybePromise._isPending()) { |
| valuesPhase[i] = 4; |
| return maybePromise._proxyPromiseArray(this, i); |
| } else if (maybePromise._isFulfilled()) { |
| ret = maybePromise._value(); |
| } else { |
| return this._reject(maybePromise._reason()); |
| } |
| } |
| |
| this._reducingIndex = i + 1; |
| this._accum = ret; |
| } |
| |
| this._resolve(isEach ? preservedValues : this._accum); |
| }; |
| |
| function reduce(promises, fn, initialValue, _each) { |
| if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a"); |
| var array = new ReductionPromiseArray(promises, fn, initialValue, _each); |
| return array.promise(); |
| } |
| |
| Promise.prototype.reduce = function (fn, initialValue) { |
| return reduce(this, fn, initialValue, null); |
| }; |
| |
| Promise.reduce = function (promises, fn, initialValue, _each) { |
| return reduce(promises, fn, initialValue, _each); |
| }; |
| }; |