143 lines
4.7 KiB
JavaScript
143 lines
4.7 KiB
JavaScript
|
"use strict";
|
||
|
module.exports = function(Promise,
|
||
|
PromiseArray,
|
||
|
apiRejection,
|
||
|
tryConvertToPromise,
|
||
|
INTERNAL) {
|
||
|
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) 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);
|
||
|
};
|
||
|
};
|