| /** |
| * 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:</p> |
| * |
| * 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 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) { |
| if (maybePromise.isPending()) { |
| maybePromise._proxyPromiseArray(this, i); |
| } |
| else { |
| maybePromise._unsetRejectionIsUnhandled(); |
| isDirectScanNeeded = true; |
| } |
| } |
| 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; |
| async.invoke(scanMethod, 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; |
| }; |