Skip to content

Commit

Permalink
Chore: Refactor Pledge.any()/race() to better match spec
Browse files Browse the repository at this point in the history
  • Loading branch information
nzakas committed Nov 5, 2020
1 parent 8bb305d commit 6acdfe5
Show file tree
Hide file tree
Showing 3 changed files with 306 additions and 27 deletions.
106 changes: 80 additions & 26 deletions src/pledge.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,15 @@

import { PledgeSymbol } from "./pledge-symbol.js";
import { PledgeReactionJob, hostEnqueuePledgeJob } from "./pledge-jobs.js";
import { isObject, isCallable, isConstructor, PledgeAggregateError } from "./utilities.js";
import {
isObject,
isCallable,
isConstructor,
PledgeAggregateError,
iteratorStep,
iteratorValue,
getIterator
} from "./utilities.js";
import {
isPledge,
createResolvingFunctions,
Expand Down Expand Up @@ -93,12 +101,12 @@ export class Pledge {

try {
const pledgeResolve = getPledgeResolve(C);
const iteratorRecord = iterable[Symbol.iterator]();
const iteratorRecord = getIterator(iterable);
const result = performPledgeAny(iteratorRecord, C, pledgeCapability, pledgeResolve);
return result;
} catch (error) {
pledgeCapability.reject(error);
return error;
return pledgeCapability.pledge;
}
}

Expand All @@ -109,7 +117,7 @@ export class Pledge {

try {
const pledgeResolve = getPledgeResolve(C);
const iteratorRecord = iterable[Symbol.iterator]();
const iteratorRecord = getIterator(iterable);
const result = performPledgeRace(iteratorRecord, C, pledgeCapability, pledgeResolve);
return result;
} catch (error) {
Expand Down Expand Up @@ -274,9 +282,9 @@ function pledgeResolve(C, x) {
function getPledgeResolve(pledgeConstructor) {

assertIsConstructor(pledgeConstructor);
const promiseResolve = pledgeConstructor.resolve;
const pledgeResolve = pledgeConstructor.resolve;

if (!isCallable(promiseResolve)) {
if (!isCallable(pledgeResolve)) {
throw new TypeError("resolve is not callable.");
}

Expand All @@ -297,32 +305,53 @@ function performPledgeAny(iteratorRecord, constructor, resultCapability, pledgeR
const remainingElementsCount = { value: 1 };
let index = 0;

for (const nextValue of iteratorRecord) {
while (true) {
let next;

try {
next = iteratorStep(iteratorRecord);
} catch (error) {
iteratorRecord.done = true;
resultCapability.reject(error);
return resultCapability.pledge;
}

errors.push(undefined);
if (next === false) {
remainingElementsCount.value = remainingElementsCount.value - 1;
if (remainingElementsCount.value === 0) {
const error = new PledgeAggregateError();
Object.defineProperty(error, "errors", {
configurable: true,
enumerable: false,
writable: true,
value: errors
});

resultCapability.reject(error);
}

return resultCapability.pledge;
}

const nextPledge = pledgeResolve(constructor, nextValue);
let nextValue;

try {
nextValue = iteratorValue(next);
} catch(error) {
iteratorRecord.done = true;
resultCapability.reject(error);
return resultCapability.pledge;
}

errors.push(undefined);
const nextPledge = pledgeResolve.call(constructor, nextValue);
const rejectElement = createPledgeAnyRejectElement(index, errors, resultCapability, remainingElementsCount);

remainingElementsCount.value = remainingElementsCount.value + 1;
nextPledge.then(resultCapability.resolve, rejectElement);
index = index + 1;
}

remainingElementsCount.value = remainingElementsCount.value - 1;
if (remainingElementsCount.value === 0) {
const error = new PledgeAggregateError();
Object.defineProperty(error, "errors", {
configurable: true,
enumerable: false,
writable: true,
value: errors
});

resultCapability.reject(error);
}

return resultCapability.pledge;
}

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -372,10 +401,35 @@ function performPledgeRace(iteratorRecord, constructor, resultCapability, pledge
assertIsConstructor(constructor);
assertIsCallable(pledgeResolve);

for (const nextValue of iteratorRecord) {
const nextPledge = pledgeResolve(constructor, nextValue);
while (true) {

let next;

try {
next = iteratorStep(iteratorRecord);
} catch (error) {
iteratorRecord.done = true;
resultCapability.reject(error);
return resultCapability.pledge;
}

if (next === false) {
iteratorRecord.done = true;
return resultCapability.pledge;
}

let nextValue;

try {
nextValue = iteratorValue(next);
} catch (error) {
iteratorRecord.done = true;
resultCapability.reject(error);
return resultCapability.pledge;
}

const nextPledge = pledgeResolve.call(constructor, nextValue);
nextPledge.then(resultCapability.resolve, resultCapability.reject);
}

return resultCapability.pledge;
}
109 changes: 108 additions & 1 deletion src/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export function isConstructor(argument) {
return typeof argument === "function" && typeof argument.prototype !== "undefined";
}


//-----------------------------------------------------------------------------
// 19.5.7 AggregateError Objects
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -92,3 +91,111 @@ export function PledgeAggregateError(errors=[], message) {

return O;
}

//-----------------------------------------------------------------------------
// 7.4.1 GetIterator ( obj [ , hint [ , method ] ] )
//-----------------------------------------------------------------------------

export function getIterator(obj, hint="sync", method) {

if (hint !== "sync" && hint !== "async") {
throw new TypeError("Invalid hint.");
}

if (method === undefined) {

if (hint === "async") {

method = obj[Symbol.asyncIterator];

if (method === undefined) {
const syncMethod = obj[Symbol.iterator];
const syncIteratorRecord = getIterator(obj, "sync", syncMethod);

// can't accurately represent CreateAsyncFromSyncIterator()
return syncIteratorRecord;
}
} else {
method = obj[Symbol.iterator];
}
}

const iterator = method.call(obj);

if (!isObject(iterator)) {
throw new TypeError("Iterator must be an object.");
}

const nextMethod = iterator.next;

return {
iterator,
nextMethod,
done: false
};

}

//-----------------------------------------------------------------------------
// 7.4.2 IteratorNext ( iteratorRecord [ , value ] )
//-----------------------------------------------------------------------------

export function iteratorNext(iteratorRecord, value) {

let result;

if (value === undefined) {
result = iteratorRecord.nextMethod.call(iteratorRecord.iterator);
} else {
result = iteratorRecord.nextMethod.call(iteratorRecord.iterator, value);
}

if (!isObject(result)) {
throw new TypeError("Result must be an object.");
}

return result;

}

//-----------------------------------------------------------------------------
// 7.4.3 IteratorComplete(iterResult)
//-----------------------------------------------------------------------------

export function iteratorComplete(iterResult) {

if (!isObject(iterResult)) {
throw new TypeError("Argument must be an object.");
}

return Boolean(iterResult.done);
}

//-----------------------------------------------------------------------------
// 7.4.4 IteratorValue(iterResult)
//-----------------------------------------------------------------------------

export function iteratorValue(iterResult) {

if (!isObject(iterResult)) {
throw new TypeError("Argument must be an object.");
}

return iterResult.value;
}

//-----------------------------------------------------------------------------
// 7.4.5 IteratorStep ( iteratorRecord )
//-----------------------------------------------------------------------------

export function iteratorStep(iteratorRecord) {

const result = iteratorNext(iteratorRecord);
const done = iteratorComplete(result);

if (done) {
return false;
}

return result;
}
Loading

0 comments on commit 6acdfe5

Please sign in to comment.