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:
Lots have changed since this post was written. Promises
, iterators/generators
and async/await
syntax are all part of the standard. Let's take a look at the evolution of running a simple async operation (e.g. setTimeout
) over the different methods.
Let's consider a simple Promise wrapper to the setTimeout
function. Then, we can implement a simple Promise chain to console.log
messages with a sleep delay.
function sleep(delay) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, delay);
} );
}
console.log('one');
sleep(1000)
.then( function () {
console.log('two');
return sleep(1000);
} )
.then( function () {
console.log('three');
} );
Now let's consider rewriting the above Promise chain using async/await syntax:
function sleep(delay) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, delay);
} );
}
(async function () {
console.log('one');
await sleep(1000);
console.log('two');
await sleep(1000);
console.log('three');
})();
Very nice. Prior to new standards, people were using https://babeljs.io to help transpile from the newer JavaScript standards to an earlier version by rewriting await/async
with iterator/generator syntax:
function sleep(delay) {
return new Promise(function (resolve, reject) {
setTimeout(resolve, delay);
} );
}
_asyncToGenerator(function *() {
console.log('one');
yield sleep(1000);
console.log('two');
yield sleep(1000);
console.log('three');
})();
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args)
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value)
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err)
}
_next(undefined)
})
}
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg)
var value = info.value
} catch (error) {
reject(error)
return
}
if (info.done) {
resolve(value)
} else {
Promise.resolve(value).then(_next, _throw)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With