Cover image by Dakota Ray on Flickr
If you read stuff about JavaScript lately, you probably already know that it gets new features every now and then. One of those are Asynchronous Iterations.
You probably already know about iterables and async/await, but if not don't worry, I'll update you first.
Iterables
Iterables are objects that have a method in the Symbol.iterator
field that returns an object with a next()
method. This can be used to get all iterable values of that object.
In case of an array, an object which is JS-built-in, it looks like this:
const a = [1, 2, 3];
const iteratorOfA = a[Symbol.iterator]();
iteratorOfA.next(); // { value: 1, done: false }
iteratorOfA.next(); // { value: 2, done: false }
iteratorOfA.next(); // { value: 3, done: false }
iteratorOfA.next(); // { value: undefined, done: true}
The nice thing is, you can use it in a for-in-loop, without all the extra syntax.
const a = [1, 2, 3];
for(let i in a) console.log(i);
But yes, this isn't too exiting, basic JavaScript stuff.
The cool part is, that you can write your own iterables:
const iterable = {
a: 1,
b: 2,
c: 3,
d: 4,
[Symbol.iterator]: function() {
const keys = Object.keys(this);
let i = 0;
return {
next: () => {
if (i == keys.length) return {value: null, done: true};
return {
value: [keys[i], this[keys[i++]]],
done: false
};
}
}
}
};
for(let item of iterable) console.log(item);
Object.keys()
only returns the non-symbol keys as an array, so Symbol.iterator
won't show up in it.
Now, when the next method of that returned object is called, I return a new key and value pair as an array. When I don't find any more pairs, I return an object with done: true
and tell the caller that I'm finished.
As you can see in the end, this new object can be used like an array in a for-in-loop.
Async/Await
A not-so-basic JavaScript feature, which is rather recent are asynchronous functions, or async/await.
Essentially it adds syntax sugar to promises.
Without async/await it would look like that:
function load(url) {
return fetch(url)
.then(response => response.json())
.(json => json.data);
}
And with you get to write code that looks synchronous again:
async function load(url) {
const response = await fetch(url);
const json = await response.json();
return json.data;
}
Asynchronous Iterations
As you can probably imagine, there are quite some asynchronous operations that won't be done with just one promise.
But you can't simply write something like that:
function processRows(filePath) {
for(let row of getRow(filePath)) {
...
}
}
Because the getRow()
call would hit the file-system, which is an asynchronous operation. You would have to read the whole file, before you could get the single rows for processing.
Or it could be a server call that ends up being paginated and you would have to send multiple of them to get all pages.
But now there is an proposal for that too!
Instead of using Symbol.iterator
and using a next-method that returns your values, you use Symbol.asyncIterator
and use a next-method that returns promises of those values.
const asyncIterable = {
a: 1,
b: 2,
c: 3,
d: 4,
[Symbol.asyncIterator]: function() {
const keys = Object.keys(this);
let i = 0;
return {
next: () => {
if (i == keys.length) return Promise.resolve({value: null, done: true});
return Promise.resolve({
value: [keys[i], this[keys[i++]]],
done: false
});
}
}
}
};
async function process() {
for await (let item of asyncIterable) console.log(item);
}
process();
And we're back to code that can be sprinkled with try/catch and all the other nice synchronous features.
As you can see, you can simply return a promise that resolves to an object with done: true
if your iterable data is finished. Like, when the server doesn't return anything anymore.
In that example the data is in-memory, but could come from anywhere now.
Conclusion
Asynchronous iterables are another step in making async/await syntax and promises more ingrained into JavaScript. They ease the work of writing asynchronous code by making it look more and more synchronous.
An alternative, which I mentioned in other posts too, are observables like provided by RxJS.
Top comments (4)
I didn't know about Iterables until I read this! This is awesome! Thank you!
Note: In the example, you left out the
()
to execute thea[Symbol.iterator]
.It should be.
const iteratorOfA = a[Symbol.iterator]()
;Yes you're right, thanks!
Thanks a lot, I needed this.
Where was this article two days ago, when I needed it!
Well, it was here :D
Thanks for the article!