ES6 has generators that return iterators:
function* range(n) {
for (let i = 0; i < n; ++i) {
yield i;
}
}
for (let x of range(10)) {
console.log(x);
}
There is a proposal for asynchronous functions that return Promises:
async function f(x) {
let y = await g(x);
return y * y;
}
f(2).then(y => {
console.log(y);
});
So what happens if I combine the two, like this:
async function* ag(n) {
for (let i = 0; i < n; ++i) {
yield i;
}
}
What does it return? Is it Promise<Iterator<Item>>
? Iterator<Promise<Item>>
? Something else? How do I consume it? I imagine there should be a corresponding for
loop, what will iterate over its result asynchronously, something like:
for (await let x of ag(10)) {
console.log(x);
}
which waits for each item to become available before trying to access the next one.
Promise<Iterator<Item>>
or Iterator<Promise<Item>>
?Neither. It's still not approved, but current implementations return something else. Kris Kowal has written an about async generators, and references Jafar Husain's AsyncGenerator proposal for ES7. EDIT: We have tc39 proposal and babel support!
Let's define some types (simplified):
interface Iterator<T> {
Iteration<T> next();
}
type Iteration<T> = { done: boolean, value: T }
We are looking for something that can be used like this:
for (;;) {
var iteration = await async_iterator.next();
if (iteration.done) {
return iteration.value;
} else {
console.log(iteration.value);
}
}
An Iterator<Promise<T>>
produces synchronous iterations, whose values are Promises. It could be used like this:
for (;;) {
var iteration = iterator_promise.next();
if (iteration.done) {
return await iteration.value;
} else {
console.log(await iteration.value);
}
}
A Promise<Iterator<T>>
is just a regular synchronous iterator, starting in the future:
var iterator = await promise_iterator;
for (;;) {
var iteration = iterator.next();
if (iteration.done) {
return iteration.value;
} else {
console.log(iteration.value);
}
}
So neither Iterator<Promise<T>>
nor Promise<Iterator<T>>
was suitable. Currently async generators return AsyncIterator
s instead:
interface AsyncIterator<T> {
Promise<Iteration<T>> next();
}
Which perfectly makes sense. Moving to the next element of the iterator is the asynchronous operation, and this can be used exactly like we wanted.
Babeljs.io already compiles async generators. Babeljs.io/repl example:
EDIT: No preset on babeljs.io compiles async generators since babel 6, babel-plugin-transform-regenerator
supports it with {asyncGenerators:true}
option.
EDIT: see transform-async-generator-functions
babel 6 plugin.
function delay(timeout, val) {
return new Promise(resolve => setTimeout(resolve, timeout, val));
}
async function* asyncGenerator() {
for (var i = 0; i < 5; i++) {
await delay(500);
yield i;
}
}
async function forAwait(iter, fn) {
for (;;) {
let iteration = await iter.next();
if (iteration.done) return iteration.value;
await fn(iteration.value);
}
}
async function main() {
console.log('Started');
await forAwait(asyncGenerator(), async item => {
await delay(100);
console.log(item);
});
console.log('End');
}
main();
There is a proposal for a convenient for await
loop for async iterators (described at Async iteration):
for await (let line of readLines(filePath)) {
print(line);
}
Update:
Unfortunately, async-await
didn't become a part of ECMAScript 2016. At least await
is mentioned a reserved word for future use.
Update:
Related proposals: