"use strict"; module.exports = function(Promise, PromiseArray, apiRejection, tryConvertToPromise, INTERNAL) { var getDomain = Promise._getDomain; var async = require("./async.js"); var util = require("./util.js"); var tryCatch = util.tryCatch; var errorObj = util.errorObj; var PENDING = {}; var EMPTY_ARRAY = []; function MappingPromiseArray(promises, fn, limit, _filter) { this.constructor$(promises); this._promise._captureStackTrace(); var domain = getDomain(); this._callback = domain === null ? fn : domain.bind(fn); this._preservedValues = _filter === INTERNAL ? new Array(this.length()) : null; this._limit = limit; this._inFlight = 0; this._queue = limit >= 1 ? [] : EMPTY_ARRAY; async.invoke(init, this, undefined); } util.inherits(MappingPromiseArray, PromiseArray); function init() {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 (values[index] === PENDING) { values[index] = value; if (limit >= 1) { this._inFlight--; this._drainQueue(); if (this._isResolved()) return; } } else { if (limit >= 1 && this._inFlight >= limit) { values[index] = value; this._queue.push(index); return; } if (preservedValues !== null) preservedValues[index] = value; var callback = this._callback; var receiver = this._promise._boundValue(); this._promise._pushContext(); var ret = tryCatch(callback).call(receiver, value, index, 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()) { if (limit >= 1) this._inFlight++; values[index] = PENDING; return maybePromise._proxyPromiseArray(this, index); } else if (maybePromise._isFulfilled()) { ret = maybePromise._value(); } else { return this._reject(maybePromise._reason()); } } values[index] = ret; } var totalResolved = ++this._totalResolved; if (totalResolved >= length) { if (preservedValues !== null) { this._filter(values, preservedValues); } else { this._resolve(values); } } }; 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) { var limit = typeof options === "object" && options !== null ? options.concurrency : 0; limit = typeof limit === "number" && isFinite(limit) && limit >= 1 ? limit : 0; return new MappingPromiseArray(promises, fn, limit, _filter); } Promise.prototype.map = function (fn, options) { if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a"); return map(this, fn, options, null).promise(); }; Promise.map = function (promises, fn, options, _filter) { if (typeof fn !== "function") return apiRejection("fn must be a function\u000a\u000a See http://goo.gl/916lJJ\u000a"); return map(promises, fn, options, _filter).promise(); }; };