| "use strict"; |
| module.exports = function(Promise, |
| PromiseArray, |
| apiRejection, |
| tryConvertToPromise, |
| INTERNAL) { |
| 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(); |
| this._callback = 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._boundTo; |
| 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(); |
| }; |
| |
| |
| }; |