168 lines
5.3 KiB
JavaScript
168 lines
5.3 KiB
JavaScript
"use strict";
|
|
module.exports = function(Promise,
|
|
PromiseArray,
|
|
apiRejection,
|
|
tryConvertToPromise,
|
|
INTERNAL,
|
|
debug) {
|
|
var getDomain = Promise._getDomain;
|
|
var util = require("./util");
|
|
var tryCatch = util.tryCatch;
|
|
var errorObj = util.errorObj;
|
|
var async = Promise._async;
|
|
|
|
function MappingPromiseArray(promises, fn, limit, _filter) {
|
|
this.constructor$(promises);
|
|
this._promise._captureStackTrace();
|
|
var domain = getDomain();
|
|
this._callback = domain === null ? fn : util.domainBind(domain, fn);
|
|
this._preservedValues = _filter === INTERNAL
|
|
? new Array(this.length())
|
|
: null;
|
|
this._limit = limit;
|
|
this._inFlight = 0;
|
|
this._queue = [];
|
|
async.invoke(this._asyncInit, this, undefined);
|
|
}
|
|
util.inherits(MappingPromiseArray, PromiseArray);
|
|
|
|
MappingPromiseArray.prototype._asyncInit = function() {
|
|
this._init$(undefined, -2);
|
|
};
|
|
|
|
MappingPromiseArray.prototype._init = function () {};
|
|
|
|
MappingPromiseArray.prototype._promiseFulfilled = function (value, index) {
|
|
var values = this._values;
|
|
var length = this.length();
|
|
var preservedValues = this._preservedValues;
|
|
var limit = this._limit;
|
|
|
|
if (index < 0) {
|
|
index = (index * -1) - 1;
|
|
values[index] = value;
|
|
if (limit >= 1) {
|
|
this._inFlight--;
|
|
this._drainQueue();
|
|
if (this._isResolved()) return true;
|
|
}
|
|
} else {
|
|
if (limit >= 1 && this._inFlight >= limit) {
|
|
values[index] = value;
|
|
this._queue.push(index);
|
|
return false;
|
|
}
|
|
if (preservedValues !== null) preservedValues[index] = value;
|
|
|
|
var promise = this._promise;
|
|
var callback = this._callback;
|
|
var receiver = promise._boundValue();
|
|
promise._pushContext();
|
|
var ret = tryCatch(callback).call(receiver, value, index, length);
|
|
var promiseCreated = promise._popContext();
|
|
debug.checkForgottenReturns(
|
|
ret,
|
|
promiseCreated,
|
|
preservedValues !== null ? "Promise.filter" : "Promise.map",
|
|
promise
|
|
);
|
|
if (ret === errorObj) {
|
|
this._reject(ret.e);
|
|
return true;
|
|
}
|
|
|
|
var maybePromise = tryConvertToPromise(ret, this._promise);
|
|
if (maybePromise instanceof Promise) {
|
|
maybePromise = maybePromise._target();
|
|
var bitField = maybePromise._bitField;
|
|
;
|
|
if (((bitField & 50397184) === 0)) {
|
|
if (limit >= 1) this._inFlight++;
|
|
values[index] = maybePromise;
|
|
maybePromise._proxy(this, (index + 1) * -1);
|
|
return false;
|
|
} else if (((bitField & 33554432) !== 0)) {
|
|
ret = maybePromise._value();
|
|
} else if (((bitField & 16777216) !== 0)) {
|
|
this._reject(maybePromise._reason());
|
|
return true;
|
|
} else {
|
|
this._cancel();
|
|
return true;
|
|
}
|
|
}
|
|
values[index] = ret;
|
|
}
|
|
var totalResolved = ++this._totalResolved;
|
|
if (totalResolved >= length) {
|
|
if (preservedValues !== null) {
|
|
this._filter(values, preservedValues);
|
|
} else {
|
|
this._resolve(values);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
MappingPromiseArray.prototype._drainQueue = function () {
|
|
var queue = this._queue;
|
|
var limit = this._limit;
|
|
var values = this._values;
|
|
while (queue.length > 0 && this._inFlight < limit) {
|
|
if (this._isResolved()) return;
|
|
var index = queue.pop();
|
|
this._promiseFulfilled(values[index], index);
|
|
}
|
|
};
|
|
|
|
MappingPromiseArray.prototype._filter = function (booleans, values) {
|
|
var len = values.length;
|
|
var ret = new Array(len);
|
|
var j = 0;
|
|
for (var i = 0; i < len; ++i) {
|
|
if (booleans[i]) ret[j++] = values[i];
|
|
}
|
|
ret.length = j;
|
|
this._resolve(ret);
|
|
};
|
|
|
|
MappingPromiseArray.prototype.preservedValues = function () {
|
|
return this._preservedValues;
|
|
};
|
|
|
|
function map(promises, fn, options, _filter) {
|
|
if (typeof fn !== "function") {
|
|
return apiRejection("expecting a function but got " + util.classString(fn));
|
|
}
|
|
|
|
var limit = 0;
|
|
if (options !== undefined) {
|
|
if (typeof options === "object" && options !== null) {
|
|
if (typeof options.concurrency !== "number") {
|
|
return Promise.reject(
|
|
new TypeError("'concurrency' must be a number but it is " +
|
|
util.classString(options.concurrency)));
|
|
}
|
|
limit = options.concurrency;
|
|
} else {
|
|
return Promise.reject(new TypeError(
|
|
"options argument must be an object but it is " +
|
|
util.classString(options)));
|
|
}
|
|
}
|
|
limit = typeof limit === "number" &&
|
|
isFinite(limit) && limit >= 1 ? limit : 0;
|
|
return new MappingPromiseArray(promises, fn, limit, _filter).promise();
|
|
}
|
|
|
|
Promise.prototype.map = function (fn, options) {
|
|
return map(this, fn, options, null);
|
|
};
|
|
|
|
Promise.map = function (promises, fn, options, _filter) {
|
|
return map(promises, fn, options, _filter);
|
|
};
|
|
|
|
|
|
};
|