Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Javascript: repetita iuvant

Javascript: repetita iuvant

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!

Luciano Mammino

May 13, 2021
Tweet

More Decks by Luciano Mammino

Other Decks in Technology

Transcript

  1. 👋 Hello, I am Luciano Senior architect nodejsdesignpatterns.com Let’s connect:

    🌎 loige.co 🐦 @loige 🧳 lucianomammino 20% OFF eBook on Packt.com 20NODEMAY
  2. How many ways do you know to do iteration in

    JavaScript? AFTERNOON TRIVIA 🤨 @loige
  3. I counted 28! 😱 • for i / for...in /

    for...of • while / do while • Array.forEach / Array.map / Array.flatMap / Array.reduce / Array.reduceRight / Array.filter / Array.find / Array.findIndex / Array.entries / Array.values / Array.every / Array.some • Object.keys / Object.values / Object.entries @loige • Iterators / Generators • Spread operator • Events • Streams • Async iterators / Async Generators • for await...of
  4. Agenda • Syntax review • Iteration protocols ◦ Iterator protocol

    ◦ Iterable protocol ◦ Generator functions ◦ Async iterator protocol ◦ Async iterable protocol @loige
  5. const judokas = [ 'Driulis Gonzalez Morales', 'Ilias Iliadis', 'Tadahiro

    Nomura', 'Anton Geesink', 'Teddy Riner', 'Ryoko Tani' ] for (const judoka of judokas) { console.log(judoka) } for...of (with arrays) @loige Output Driulis Gonzalez Morales Ilias Iliadis Tadahiro Nomura Anton Geesink Teddy Riner Ryoko Tani
  6. 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
  7. const medals = new Set([ 'gold', 'silver', 'bronze' ]) for

    (const medal of medals) { console.log(medal) } for...of (with Set) @loige Output gold silver bronze
  8. 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
  9. 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
  10. 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)
  11. const countdown = [3, 2, 1, 0] // spread into

    array const from5to0 = [5, 4, ...countdown] console.log(from5to0) // [ 5, 4, 3, 2, 1, 0 ] // spread function arguments console.log('countdown data:', ...countdown) // countdown data: 3 2 1 0 Spread operator @loige
  12. 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
  13. 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
  14. 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
  15. function createCountdown (from) { let nextVal = from return {

    next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } Countdown iterator @loige const countdown = createCountdown(3) console.log(countdown.next()) // { done: false, value: 3 } console.log(countdown.next()) // { done: false, value: 2 } console.log(countdown.next()) // { done: false, value: 1 } console.log(countdown.next()) // { done: false, value: 0 } console.log(countdown.next()) // { done: true }
  16. Iterable protocol An object is iterable if it implements the

    @@iterator* method, a zero-argument function that returns an iterator. * Symbol.iterator @loige
  17. 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
  18. const iterableIterator = { next() { return { done: false,

    value: "hello" } }, [Symbol.iterator]() { return this } } YES! 🤩 @loige iterableIterator.next() // { done: false, value: "hello" } for (const value of iterableIterator) { console.log(value) } // hello // hello // hello // ...
  19. A generator function “produces” an object that is both an

    iterator and an iterable! 🤯 @loige Generators
  20. function * createCountdown (from) { for (let i = from;

    i >= 0; i--) { yield i } } Using generator functions @loige // As iterator const countdown = createCountdown(3) console.log(countdown.next()) // { done: false, value: 3 } console.log(countdown.next()) // { done: false, value: 2 } console.log(countdown.next()) // { done: false, value: 1 } console.log(countdown.next()) // { done: false, value: 0 } console.log(countdown.next()) // { done: true } // As iterable const countdown = createCountdown(3) for (const value of countdown) { console.log(value) } // 3 // 2 // 1 // 0
  21. 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
  22. import { setTimeout } from 'timers/promises' function createAsyncCountdown (from, delay

    = 1000) { let nextVal = from return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } Countdown Async iterator @loige const countdown = createAsyncCountdown(3) console.log(await countdown.next()) // { done: false, value: 3 } console.log(await countdown.next()) // { done: false, value: 2 } console.log(await countdown.next()) // { done: false, value: 1 } console.log(await countdown.next()) // { done: false, value: 0 } console.log(await countdown.next()) // { done: true }
  23. 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
  24. 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 }
  25. 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 }
  26. 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
  27. 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