blob: 6d43606c30c9c19d48caa12269c25c4b47402b13 [file] [log] [blame]
/**
* 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;
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;
};