| "use strict"; |
| module.exports = function(Promise, |
| PromiseArray, |
| apiRejection, |
| tryConvertToPromise, |
| INTERNAL, |
| debug) { |
| var util = require("./util"); |
| var tryCatch = util.tryCatch; |
| |
| function ReductionPromiseArray(promises, fn, initialValue, _each) { |
| this.constructor$(promises); |
| var context = Promise._getContext(); |
| this._fn = util.contextBind(context, fn); |
| if (initialValue !== undefined) { |
| initialValue = Promise.resolve(initialValue); |
| initialValue._attachCancellationCallback(this); |
| } |
| this._initialValue = initialValue; |
| this._currentCancellable = null; |
| if(_each === INTERNAL) { |
| this._eachValues = Array(this._length); |
| } else if (_each === 0) { |
| this._eachValues = null; |
| } else { |
| this._eachValues = undefined; |
| } |
| this._promise._captureStackTrace(); |
| this._init$(undefined, -5); |
| } |
| util.inherits(ReductionPromiseArray, PromiseArray); |
| |
| ReductionPromiseArray.prototype._gotAccum = function(accum) { |
| if (this._eachValues !== undefined && |
| this._eachValues !== null && |
| accum !== INTERNAL) { |
| this._eachValues.push(accum); |
| } |
| }; |
| |
| ReductionPromiseArray.prototype._eachComplete = function(value) { |
| if (this._eachValues !== null) { |
| this._eachValues.push(value); |
| } |
| return this._eachValues; |
| }; |
| |
| ReductionPromiseArray.prototype._init = function() {}; |
| |
| ReductionPromiseArray.prototype._resolveEmptyArray = function() { |
| this._resolve(this._eachValues !== undefined ? this._eachValues |
| : this._initialValue); |
| }; |
| |
| ReductionPromiseArray.prototype.shouldCopyValues = function () { |
| return false; |
| }; |
| |
| ReductionPromiseArray.prototype._resolve = function(value) { |
| this._promise._resolveCallback(value); |
| this._values = null; |
| }; |
| |
| ReductionPromiseArray.prototype._resultCancelled = function(sender) { |
| if (sender === this._initialValue) return this._cancel(); |
| if (this._isResolved()) return; |
| this._resultCancelled$(); |
| if (this._currentCancellable instanceof Promise) { |
| this._currentCancellable.cancel(); |
| } |
| if (this._initialValue instanceof Promise) { |
| this._initialValue.cancel(); |
| } |
| }; |
| |
| ReductionPromiseArray.prototype._iterate = function (values) { |
| this._values = values; |
| var value; |
| var i; |
| var length = values.length; |
| if (this._initialValue !== undefined) { |
| value = this._initialValue; |
| i = 0; |
| } else { |
| value = Promise.resolve(values[0]); |
| i = 1; |
| } |
| |
| this._currentCancellable = value; |
| |
| for (var j = i; j < length; ++j) { |
| var maybePromise = values[j]; |
| if (maybePromise instanceof Promise) { |
| maybePromise.suppressUnhandledRejections(); |
| } |
| } |
| |
| if (!value.isRejected()) { |
| for (; i < length; ++i) { |
| var ctx = { |
| accum: null, |
| value: values[i], |
| index: i, |
| length: length, |
| array: this |
| }; |
| |
| value = value._then(gotAccum, undefined, undefined, ctx, undefined); |
| |
| if ((i & 127) === 0) { |
| value._setNoAsyncGuarantee(); |
| } |
| } |
| } |
| |
| if (this._eachValues !== undefined) { |
| value = value |
| ._then(this._eachComplete, undefined, undefined, this, undefined); |
| } |
| value._then(completed, completed, undefined, value, this); |
| }; |
| |
| 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); |
| }; |
| |
| function completed(valueOrReason, array) { |
| if (this.isFulfilled()) { |
| array._resolve(valueOrReason); |
| } else { |
| array._reject(valueOrReason); |
| } |
| } |
| |
| function reduce(promises, fn, initialValue, _each) { |
| if (typeof fn !== "function") { |
| return apiRejection("expecting a function but got " + util.classString(fn)); |
| } |
| var array = new ReductionPromiseArray(promises, fn, initialValue, _each); |
| return array.promise(); |
| } |
| |
| function gotAccum(accum) { |
| this.accum = accum; |
| this.array._gotAccum(accum); |
| var value = tryConvertToPromise(this.value, this.array._promise); |
| if (value instanceof Promise) { |
| this.array._currentCancellable = value; |
| return value._then(gotValue, undefined, undefined, this, undefined); |
| } else { |
| return gotValue.call(this, value); |
| } |
| } |
| |
| function gotValue(value) { |
| var array = this.array; |
| var promise = array._promise; |
| var fn = tryCatch(array._fn); |
| promise._pushContext(); |
| var ret; |
| if (array._eachValues !== undefined) { |
| ret = fn.call(promise._boundValue(), value, this.index, this.length); |
| } else { |
| ret = fn.call(promise._boundValue(), |
| this.accum, value, this.index, this.length); |
| } |
| if (ret instanceof Promise) { |
| array._currentCancellable = ret; |
| } |
| var promiseCreated = promise._popContext(); |
| debug.checkForgottenReturns( |
| ret, |
| promiseCreated, |
| array._eachValues !== undefined ? "Promise.each" : "Promise.reduce", |
| promise |
| ); |
| return ret; |
| } |
| }; |