How do JavaScript async iterators work?
A while ago, I described iterators in JavaScript.
In this post, I describe a limitation of iterators: the provider must provide its values synchronously.
Then show how JavaScript relaxes this iterator contract
to allow providers to provide their values asynchronously.
Finally I show modern JavaScript syntax forms for consuming async iterables.
As a recap,
an iterable
is anything that you can call [Symbol.iterator]()
on
and get an iterator back.
An iterator is anything that you can .next()
on repeatedly,
each time getting something back that looks like { done: bool, value: any }
.
But typically, you call neither [Symbol.iterator]()
nor .next()
.
Instead use the for (const x of iterable) { console.log(x); }
syntax form,
which desugars to something like this:
const iterator = iterable[Symbol.iterator]();
let __v = iterator.next();
while (!v.done) {
const x = v.value;
console.log(x);
v = iterator.next();
}
All of this consumer code is synchronous.
The for...of
form is synchronous;
it will run to completion before yielding.
It uses the value
returned from .next()
synchronously.
Now, you can use a normal iterator in an async fashion.
For example, this will print all the values of a normal iterator,
one per second,
and you could do this concurrently with other work:
const iterator = myIterable[Symbol.iterator]();
function loop() {
const v = iterator.next();
if (!v.done) {
console.log(v.value);
setTimeout(loop, 1000);
}
}
loop();
But this is besides the point.
A normal iterator, since it doesn’t know how it is going to be used,
has to be able to provide all of its values synchronously,
if asked.
But what if our iterator can’t fulfil that?
For example, imagine an iterator over a sequence of web pages,
or the arbitrary stream of data coming over a network connection.
These cannot provide everything synchronously
except by blocking the entire process.
What if instead of returning { done: bool, value: any }
,
our iterator returned { done: bool, value: Promise<any> }
?
This is close, but no cigar.
Note the done
value is still synchronous,
so the iterator must be able to know in advance how many values it will provide.
Imagine the “network connection” iterator:
it cannot know in advance when the stream will end.
Instead, these async iterators must provide something like Promise<{ done: bool, value: any }>
.
The consumer must wait for each promise to resolve,
then check the done
property,
before calling .next()
again.
This is how JavaScript async iterators work.
Because this is an entirely new contract,
async iterables expose their async iterator under a different key,
asyncIterable[Symbol.asyncIterator]()
.
Here’s an async iterable
that yields the values 0
to 99
,
with a one-second delay between calling .next()
and yielding the value:
const myAsyncIterable = {
i: 0,
[Symbol.asyncIterator]: function() {
return {
next: () => {
return new Promise(resolve => {
setTimeout(() => {
resolve({ done: this.i >= 100, value: this.i++ });
}, 10);
});
}
};
}
};
And here’s how to use this async iterable,
and print its values as fast as it provides them:
const asyncIterator = myAsyncIterable[Symbol.asyncIterator]();
function loop() {
asyncIterator.next().then(v => {
if (!v.done) {
console.log(v.value);
loop();
}
});
}
loop();
Note that the async iterator contract
forces the consumer to loop in an async fashion.
(The consumer can still call .next()
in a hot loop on an async iterator,
but this breaks the contract,
and it means the consumer can’t access the done
value,
so it doesn’t know when to end looping.)
Because this is a Promise-based API,
you can use async
/await
to consume it,
like this:
const it = myAsyncIterable[Symbol.asyncIterator]();
for (let v = await it.next(); !v.done; v = await it.next()) {
console.log(v.value);
}
But for this pattern of consumption,
JavaScript provides a dedicated for await
syntax form.
This does the same thing:
for await (const x of myAsyncIterable) {
console.log(x);
}
Similar posts
How do classes work in JavaScript?
JavaScript class
syntax is just syntactic sugar over the traditional prototype-based inheritance. Deconstructing a simple class
reveals the underlying constructor functions, prototype assignment, and use of super
to call the superclass’s constructor. 2020-11-03
How do JavaScript prototypes work?
JavaScript has two different “prototype” concepts: an own property with the string key “prototype”, and a parent slot. Don’t confuse the two! 2020-11-02
How does the Node.js REPL display previews?
Node.js REPL uses the inspector
module to safely evaluate expressions as you type, using V8’s evaluation with throwOnSideEffect: true
to avoid executing harmful code. 2020-10-05
How to publish an npm package
Publishing an npm package @jameshfisher/numsyslib
with a stringifyRoman
function that converts a number to a Roman numeral. 2020-10-01
What is npm?
NPM is Node.js’s package system, allowing installation of arbitrary stuff, not just Node.js modules. Packages are folders, tarballs, URLs, or version identifiers published to the NPM registry. 2020-09-30
How do ECMAScript modules work in Node.js?
ECMAScript modules and CommonJS modules in Node.js are interoperable. Node.js looks at file extensions and package.json
fields to determine the module type. ECMAScript modules can only use import
, while CommonJS modules can only use require
. 2020-09-29
More by Jim
What does the dot do in JavaScript?
foo.bar
, foo.bar()
, or foo.bar = baz
- what do they mean? A deep dive into prototypical inheritance and getters/setters. 2020-11-01
Smear phishing: a new Android vulnerability
Trick Android to display an SMS as coming from any contact. Convincing phishing vuln, but still unpatched. 2020-08-06
A probabilistic pub quiz for nerds
A “true or false” quiz where you respond with your confidence level, and the optimal strategy is to report your true belief. 2020-04-26
Time is running out to catch COVID-19
Simulation shows it’s rational to deliberately infect yourself with COVID-19 early on to get treatment, but after healthcare capacity is exceeded, it’s better to avoid infection. Includes interactive parameters and visualizations. 2020-03-14
The inception bar: a new phishing method
A new phishing technique that displays a fake URL bar in Chrome for mobile. A key innovation is the “scroll jail” that traps the user in a fake browser. 2019-04-27
The hacker hype cycle
I got started with simple web development, but because enamored with increasingly esoteric programming concepts, leading to a “trough of hipster technologies” before returning to more productive work. 2019-03-23
Project C-43: the lost origins of asymmetric crypto
Bob invents asymmetric cryptography by playing loud white noise to obscure Alice’s message, which he can cancel out but an eavesdropper cannot. This idea, published in 1944 by Walter Koenig Jr., is the forgotten origin of asymmetric crypto. 2019-02-16
How Hacker News stays interesting
Hacker News buried my post on conspiracy theories in my family due to overheated discussion, not censorship. Moderation keeps the site focused on interesting technical content. 2019-01-26
My parents are Flat-Earthers
For decades, my parents have been working up to Flat-Earther beliefs. From Egyptology to Jehovah’s Witnesses to theories that human built the Moon billions of years in the future. Surprisingly, it doesn’t affect their successful lives very much. For me, it’s a fun family pastime. 2019-01-20
The dots do matter: how to scam a Gmail user
Gmail’s “dots don’t matter” feature lets scammers create an account on, say, Netflix, with your email address but different dots. Results in convincing phishing emails. 2018-04-07
The sorry state of OpenSSL usability
OpenSSL’s inadequate documentation, confusing key formats, and deprecated interfaces make it difficult to use, despite its importance. 2017-12-02
I hate telephones
I hate telephones. Some rational reasons: lack of authentication, no spam filtering, forced synchronous communication. But also just a visceral fear. 2017-11-08
The Three Ts of Time, Thought and Typing: measuring cost on the web
Businesses often tout “free” services, but the real costs come in terms of time, thought, and typing required from users. Reducing these “Three Ts” is key to improving sign-up flows and increasing conversions. 2017-10-26
Granddad died today
Granddad died. The unspoken practice of death-by-dehydration in the NHS. The Liverpool Care Pathway. Assisted dying in the UK. The importance of planning in end-of-life care. 2017-05-19
How do I call a program in C, setting up standard pipes?
A C function to create a new process, set up its standard input/output/error pipes, and return a struct containing the process ID and pipe file descriptors. 2017-02-17
Your syntax highlighter is wrong
Syntax highlighters make value judgments about code. Most highlighters judge that comments are cruft, and try to hide them. Most diff viewers judge that code deletions are bad. 2014-05-11
Want to build a fantastic product using LLMs? I work at
Granola where we're building the future IDE for knowledge work. Come and work with us!
Read more or
get in touch! This page copyright James Fisher 2020. Content is not associated with my employer. Found an error? Edit this page.