/** * Copyright (c) 2014 Petka Antonov * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions:

* * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * */ "use strict"; module.exports = function(Promise, INTERNAL) { var ASSERT = require("./assert.js"); var canAttach = require("./errors.js").canAttach; var util = require("./util.js"); var async = require("./async.js"); var hasOwn = {}.hasOwnProperty; var isArray = util.isArray; function toResolutionValue(val) { switch(val) { case -1: return void 0; case -2: return []; case -3: return {}; } } function PromiseArray(values, caller, boundTo) { var promise = this._promise = new Promise(INTERNAL); var parent = void 0; if (Promise.is(values)) { parent = values; if (values._cancellable()) { promise._setCancellable(); promise._cancellationParent = values; } if (values._isBound()) { promise._setBoundTo(boundTo); } } promise._setTrace(caller, parent); this._values = values; this._length = 0; this._totalResolved = 0; this._init(void 0, -2); } PromiseArray.PropertiesPromiseArray = function() {}; PromiseArray.prototype.length = function PromiseArray$length() { return this._length; }; PromiseArray.prototype.promise = function PromiseArray$promise() { return this._promise; }; PromiseArray.prototype._init = function PromiseArray$_init(_, resolveValueIfEmpty) { var values = this._values; if (Promise.is(values)) { if (values.isFulfilled()) { values = values._settledValue; if (!isArray(values)) { var err = new Promise.TypeError("expecting an array, a promise or a thenable"); this.__hardReject__(err); return; } this._values = values; } else if (values.isPending()) { values._then( this._init, this._reject, void 0, this, resolveValueIfEmpty, this.constructor ); return; } else { this._reject(values._settledValue); return; } } if (values.length === 0) { this._resolve(toResolutionValue(resolveValueIfEmpty)); return; } var len = values.length; var newLen = len; var newValues; if (this instanceof PromiseArray.PropertiesPromiseArray) { newValues = this._values; } else { newValues = new Array(len); } var isDirectScanNeeded = false; for (var i = 0; i < len; ++i) { var promise = values[i]; if (promise === void 0 && !hasOwn.call(values, i)) { newLen--; continue; } var maybePromise = Promise._cast(promise, void 0, void 0); if (maybePromise instanceof Promise && maybePromise.isPending()) { maybePromise._proxyPromiseArray(this, i); } else { isDirectScanNeeded = true; } newValues[i] = maybePromise; } if (newLen === 0) { if (resolveValueIfEmpty === -2) { this._resolve(newValues); } else { this._resolve(toResolutionValue(resolveValueIfEmpty)); } return; } this._values = newValues; this._length = newLen; if (isDirectScanNeeded) { var scanMethod = newLen === len ? this._scanDirectValues : this._scanDirectValuesHoled; scanMethod.call(this, len); } }; PromiseArray.prototype._settlePromiseAt = function PromiseArray$_settlePromiseAt(index) { var value = this._values[index]; if (!Promise.is(value)) { this._promiseFulfilled(value, index); } else if (value.isFulfilled()) { this._promiseFulfilled(value._settledValue, index); } else if (value.isRejected()) { this._promiseRejected(value._settledValue, index); } }; PromiseArray.prototype._scanDirectValuesHoled = function PromiseArray$_scanDirectValuesHoled(len) { for (var i = 0; i < len; ++i) { if (this._isResolved()) { break; } if (hasOwn.call(this._values, i)) { this._settlePromiseAt(i); } } }; PromiseArray.prototype._scanDirectValues = function PromiseArray$_scanDirectValues(len) { for (var i = 0; i < len; ++i) { if (this._isResolved()) { break; } this._settlePromiseAt(i); } }; PromiseArray.prototype._isResolved = function PromiseArray$_isResolved() { return this._values === null; }; PromiseArray.prototype._resolve = function PromiseArray$_resolve(value) { this._values = null; this._promise._fulfill(value); }; PromiseArray.prototype.__hardReject__ = PromiseArray.prototype._reject = function PromiseArray$_reject(reason) { this._values = null; var trace = canAttach(reason) ? reason : new Error(reason + ""); this._promise._attachExtraTrace(trace); this._promise._reject(reason, trace); }; PromiseArray.prototype._promiseProgressed = function PromiseArray$_promiseProgressed(progressValue, index) { if (this._isResolved()) return; this._promise._progress({ index: index, value: progressValue }); }; PromiseArray.prototype._promiseFulfilled = function PromiseArray$_promiseFulfilled(value, index) { if (this._isResolved()) return; this._values[index] = value; var totalResolved = ++this._totalResolved; if (totalResolved >= this._length) { this._resolve(this._values); } }; PromiseArray.prototype._promiseRejected = function PromiseArray$_promiseRejected(reason, index) { if (this._isResolved()) return; this._totalResolved++; this._reject(reason); }; return PromiseArray; };