In this talk we will take a trip into the world of iteration in JavaScript. If we would be talking only about `while`, `do while`, `for`, `Array.map`, `Array.forEach` and so on, this talk would be rather boring, so we will focus on the JavaScript iteration protocols and learn how to build our custom Async Iterators and generators!
10. const judoka = 'Ryoko Tani'
for (const char of judoka) {
console.log(char)
}
for...of (with strings)
@loige
Output
R
y
o
k
o
T
a
n
i
11. const medals = new Set([
'gold',
'silver',
'bronze'
])
for (const medal of medals) {
console.log(medal)
}
for...of (with Set)
@loige
Output
gold
silver
bronze
12. const medallists = new Map([
['Teddy Riner', 33],
['Driulis Gonzalez Morales', 16],
['Ryoko Tani', 16],
['Ilias Iliadis', 15]
])
for (const [judoka, medals] of medallists) {
console.log(`${judoka} has won ${medals} medals`)
}
for...of (with Map)
@loige
Output
Teddy Riner has won 33 medals
Driulis Gonzalez Morales has won 16
medals
Ryoko Tani has won 16 medals
Ilias Iliadis has won 15 medals
13. const medallists = {
'Teddy Riner': 33,
'Driulis Gonzalez Morales': 16,
'Ryoko Tani': 16,
'Ilias Iliadis': 15
}
for (const [judoka, medals] of Object.entries(medallists)) {
console.log(`${judoka} has won ${medals} medals`)
}
for...of (with objects & Object.entries)
@loige
Output
Teddy Riner has won 33 medals
Driulis Gonzalez Morales has won 16
medals
Ryoko Tani has won 16 medals
Ilias Iliadis has won 15 medals
14. const medallists = {
'Teddy Riner': 33,
'Driulis Gonzalez Morales': 16,
'Ryoko Tani': 16,
'Ilias Iliadis': 15
}
for (const [judoka, medals] of medallists) {
console.log(`${judoka} has won ${medals} medals`)
}
for...of (with object literals)
@loige
ERROR!
for (const [judoka, medals] of medallists) {
^
TypeError: medallists is not iterable
at Object. (.../05-for-of-object.js:8:32)
16. import { DynamoDBClient, paginateListTables } from '@aws-sdk/client-dynamodb'
const client = new DynamoDBClient({});
for await (const page of paginateListTables({ client }, {})) {
// page.TableNames is an array of table names
for (const tableName of page.TableNames) {
console.log(tableName)
}
}
for await...of (async iterable)
@loige
18. Why are iteration protocols important?
● Unified/interoperable approach to iteration
○ Use for...of, for await...of and spread operator
● You can create your own custom iterators/iterables
● Sequential iteration made easy (both sync and async)
@loige
19. Iterator protocol
In JavaScript, an object is an iterator if it has a next() method. Every
time you call it, it returns an object with the keys done (boolean) and
value.
@loige
21. Iterable protocol
An object is iterable if it implements the @@iterator* method, a
zero-argument function that returns an iterator.
* Symbol.iterator
@loige
22. function createCountdown (from) {
let nextVal = from
return {
[Symbol.iterator]: () => ({
next () {
if (nextVal < 0) {
return { done: true }
}
return {
done: false,
value: nextVal--
}
}
})
}
}
Countdown iterable
@loige
const countdown = createCountdown(3)
for (const value of countdown) {
console.log(value)
}
// 3
// 2
// 1
// 0
23. Can an object be both
an iterator and an iterable? 😎
@loige
28. Async Iterator protocol
An object is an async iterator if it has a next() method. Every time
you call it, it returns a promise that resolves to an object with the
keys done (boolean) and value.
@loige
31. Async Iterable protocol
An object is an async iterable if it implements the
@@asyncIterator* method, a zero-argument function that returns
an async iterator.
* Symbol.asyncIterator
@loige
32. Countdown Async iterable
import { setTimeout } from 'timers/promises'
function createAsyncCountdown (from, delay = 1000) {
return {
[Symbol.asyncIterator]: async function () {
return {
async next () {
await setTimeout(delay)
if (nextVal < 0) {
return { done: true }
}
return { done: false, value: nextVal-- }
}
}
}
}
}
@loige
const countdown = createAsyncCountdown(3)
for await (const value of countdown) {
console.log(value) // 3 ... 2 ... 1 ... 0
}
33. Countdown Async iterator/iterable with generators!
import { setTimeout } from 'timers/promises'
async function * createAsyncCountdown (from, delay = 1000) {
for (let i = from; i >= 0; i--) {
await setTimeout(delay)
yield i
}
}
@loige
const countdown = createAsyncCountdown(3)
for await (const value of countdown) {
console.log(value) // 3 ... 2 ... 1 ... 0
}
34.
35. When to use async iterators
● Sequential iteration pattern
● Data arriving in order over time
● You need to complete processing the current “chunk” before you
can request the next one
● Example: paginated iteration!
@loige
36.
37. Want to learn more?
● nodejsdesignpatterns.com - possibly a great book 😇
● nodejsdesignpatterns.com/blog/javascript-async-iterators/ - In-depth article
on all things iterators
● loige.link/async-it - Finding a lost song with Node.js & async iterators
@loige