|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"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; |
|
}; |
|
|