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!

F3a6662b3cd161c3c2f13604965ed0f2?s=128

Luciano Mammino

May 13, 2021
Tweet

Transcript

  1. JavaScript: repetita iuvant Luciano Mammino @loige

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

    🌎 loige.co 🐦 @loige 🧳 lucianomammino 20% OFF eBook on Packt.com 20NODEMAY
  3. We are business focused technologists that deliver. Accelerated Serverless |

    AI as a Service | Platform Modernisation
  4. 👇 Get the slides (and click around…) loige.link/devcast2 @loige

  5. How many ways do you know to do iteration in

    JavaScript? AFTERNOON TRIVIA 🤨 @loige
  6. 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
  7. Agenda • Syntax review • Iteration protocols ◦ Iterator protocol

    ◦ Iterable protocol ◦ Generator functions ◦ Async iterator protocol ◦ Async iterable protocol @loige
  8. Syntax review for...of / spread operator / for await...of @loige

  9. 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
  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)
  15. 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
  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
  17. Iteration protocols @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
  20. 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 }
  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
  24. 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 // ...
  25. A generator function “produces” an object that is both an

    iterator and an iterable! 🤯 @loige Generators
  26. 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
  27. Well what about async iteration? 🤨

  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
  29. 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 }
  30. None
  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. None
  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. None
  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
  38. Thank you! loige.link/devcast2 @loige 20% OFF eBook on Packt.com 20NODEMAY