Source code
Revision control
Copy as Markdown
Other Tools
// |reftest| skip-if(!xulRuntime.shell) -- needs drainJobQueue
// Basic Smoke Test
async function* asyncGen(n) {
for (let i = 0; i < n; i++) {
yield i * 2;
}
}
let done = false;
Array.fromAsync(asyncGen(4)).then((x) => {
assertEq(Array.isArray(x), true);
assertEq(x.length, 4);
assertEq(x[0], 0);
assertEq(x[1], 2);
assertEq(x[2], 4);
assertEq(x[3], 6);
done = true;
}
);
drainJobQueue();
assertEq(done, true);
(async function () {
class InterruptableAsyncIterator {
count = 0
closed = false
throwAfter = NaN
constructor(n, throwAfter = NaN) {
this.count = n;
this.throwAfter = throwAfter;
}
[Symbol.asyncIterator] = function () {
return {
iter: this,
i: 0,
async next() {
if (this.i > this.iter.throwAfter) {
throw "Exception"
}
if (this.i++ < this.iter.count) {
return Promise.resolve({ done: false, value: this.i - 1 });
}
return Promise.resolve({ done: true, value: undefined });
},
async return(x) {
this.iter.closed = true;
return { value: x, done: true };
}
}
}
}
var one = await Array.fromAsync(new InterruptableAsyncIterator(2));
assertEq(one.length, 2)
assertEq(one[0], 0);
assertEq(one[1], 1);
var two = new InterruptableAsyncIterator(10, 2);
var threw = false;
try {
var res = await Array.fromAsync(two);
} catch (e) {
threw = true;
assertEq(e, "Exception");
}
assertEq(threw, true);
// The iterator is not closed unless we have an abrupt completion while mapping.
assertEq(two.closed, false);
// Test throwing while mapping: Iterator should be closed.
var three = new InterruptableAsyncIterator(10, 9);
threw = false;
try {
var res = await Array.fromAsync(three, (x) => {
if (x > 3) {
throw "Range"
}
return x;
});
} catch (e) {
assertEq(e, "Range");
threw = true;
}
assertEq(threw, true);
assertEq(three.closed, true);
var sync = await Array.fromAsync([1, 2, 3]);
assertEq(sync.length, 3);
assertEq(sync[0], 1)
assertEq(sync[1], 2)
assertEq(sync[2], 3)
let closed_frozen = false;
class Frozen {
constructor(x) {
this.count = x;
Object.freeze(this);
}
[Symbol.asyncIterator] = function () {
return {
iter: this,
i: 0,
async next() {
if (this.i++ < this.iter.count) {
return Promise.resolve({ done: false, value: this.i - 1 });
}
return Promise.resolve({ done: true, value: undefined });
},
async return(x) {
// Can't use Frozen instance, becuse frozen is frozen.
closed_frozen = true;
return { value: x, done: true };
}
}
}
}
// We should close the iterator when define property throws.
// Test by defining into a frozen object.
var frozen = new Frozen(10);
threw = false;
try {
var result = await Array.fromAsync.call(Frozen, frozen);
} catch (e) {
threw = true;
}
assertEq(threw, true);
assertEq(closed_frozen, true);
})();
drainJobQueue();
(async function () {
var badSyncIterator = {
[Symbol.iterator]() {
return null;
}
};
var badAsyncIterator = {
[Symbol.asyncIterator]() {
return null;
}
};
async function errorMessage(fn) {
try {
await fn();
} catch (e) {
return e.message;
}
throw new Error("missing error");
}
// Ensure Array.from and Array.fromAsync use consistent error reporting.
var expected = await errorMessage(() => Array.from(badSyncIterator));
var actual = await errorMessage(() => Array.fromAsync(badSyncIterator));
assertEq(actual, expected);
// Ensure for-of iteration and Array.fromAsync use consistent error reporting.
var expected = await errorMessage(() => { for (var _ of badSyncIterator); });
var actual = await errorMessage(() => Array.fromAsync(badSyncIterator));
assertEq(actual, expected);
// Ensure await for-of iteration and Array.fromAsync use consistent error reporting.
var expected = await errorMessage(async () => { for await (var _ of badAsyncIterator); });
var actual = await errorMessage(() => Array.fromAsync(badAsyncIterator));
assertEq(actual, expected);
})();
drainJobQueue();
(async function () {
function* gen() {
for (let i = 0; i < 4; ++i) {
yield Promise.resolve(i);
}
};
var array = await Array.fromAsync(gen());
// Promise values are unwrapped via AsyncFromSyncIterator.
assertEqArray(array, [0, 1, 2, 3]);
})();
drainJobQueue();
(async function () {
var badSyncIterator = {
[Symbol.iterator]: 123,
};
var badAsyncIterator = {
[Symbol.asyncIterator]: 123,
};
async function errorMessage(fn) {
try {
await fn();
} catch (e) {
return e.message;
}
throw new Error("missing error");
}
// Ensure Array.from and Array.fromAsync use consistent error reporting.
var expected = await errorMessage(() => Array.from(badSyncIterator));
var actual = await errorMessage(() => Array.fromAsync(badSyncIterator));
assertEq(actual.endsWith("is not iterable"), expected.endsWith("is not iterable"));
// Ensure for-of iteration and Array.fromAsync use consistent error reporting.
var expected = await errorMessage(() => { for (var _ of badSyncIterator); });
var actual = await errorMessage(() => Array.fromAsync(badSyncIterator));
assertEq(actual.endsWith("is not iterable"), expected.endsWith("is not iterable"));
// Ensure await for-of iteration and Array.fromAsync use consistent error reporting.
var expected = await errorMessage(async () => { for await (var _ of badAsyncIterator); });
var actual = await errorMessage(() => Array.fromAsync(badAsyncIterator));
assertEq(actual.endsWith("is not iterable"), expected.endsWith("is not iterable"));
})();
drainJobQueue();
var g = newGlobal();
g.asyncGen = asyncGen;
var p = g.evaluate(`
Array.fromAsync(asyncGen(4))
`)
p.then((x) => {
assertEq(x instanceof Array, false); // Should use the other global's Array.
assertEq(x instanceof g.Array, true);
})
drainJobQueue();
var g2 = newGlobal({ newCompartment: true });
g2.asyncGen = asyncGen;
var p = g2.evaluate(`
Array.fromAsync(asyncGen(4))
`)
p.then((x) => {
assertEq(x instanceof Array, false); // Should use the other global's Array.
assertEq(x instanceof g2.Array, true);
nukeCCW(x); // this will throw if x happens to not be a CCW (it should be!)
})
drainJobQueue();
// Test having a CCW 'this' value.
g2.obj = {};
var p2 = g2.evaluate(`
Array.fromAsync.call(obj, asyncGen(4))
`)
p2.then((x) => {
assertEq(x instanceof Array, false); // Should use the other global's Array.
assertEq(x instanceof g2.Array, true);
nukeCCW(x);
})
drainJobQueue();
// Verify user promise resolution behaviour.
var myThenCalled = false;
var obj = { then: () => { myThenCalled = true; } }
function* genO() {
yield obj;
return;
}
var res = Array.fromAsync(genO());
res.then((x) => {
assertEq(x[0], obj);
assertEq(myThenCalled, true);
});
drainJobQueue();
function* thrower() {
throw new Error();
}
g2.thrower = thrower;
var p = g2.evaluate(`Array.fromAsync(thrower())`)
p.catch((e) => {
assertEq(e instanceof Error, true, "Should throw an error from the current global");
})
drainJobQueue();
p = g2.evaluate(`Array.fromAsync(thrower, 1)`);
p.catch((e) => assertEq(e instanceof g2.Error, true, "Should throw error from g2"))
drainJobQueue();
if (typeof reportCompare === 'function')
reportCompare(true, true);