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.
183 lines
5.2 KiB
183 lines
5.2 KiB
"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; |
|
} |
|
};
|
|
|