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

Reiterating JavaScript & Node.js iterators

Reiterating JavaScript & Node.js iterators

In this talk we will revisit the iteration protocols offered by JavaScript (iterable, iterator, async iterable and async iterator). We will explore generators and async generators as well. We will use this opportunity to discuss some interesting applications of these patterns in Node.js and we will compare them with more traditional approaches like Event Emitters and Node.js Streams. Finally, we will discuss some interesting integrations between traditional approaches and iterators and show some patterns and anti-patterns.

Luciano Mammino

June 16, 2021
Tweet

More Decks by Luciano Mammino

Other Decks in Technology

Transcript

  1. How many ways do you know to "do iteration" in

    JavaScript & Node.js? 😰 @loige 3
  2. Array.forEach / Array.map / Array.flatMap / Array.reduce / Array.reduceRight /

    Array.filter / Array.find / Array.findIndex / Array.entries / Array.values / Array.every / Array.some @loige 6
  3. 📝 AGENDA Iteration protocols... What? Why? Syntax review Iteration protocols

    Iterator protocol Iterable protocol Generator functions Async iterator protocol Async iterable protocol Tips & tricks @loige 14
  4. Let me introduce myself... I'm Luciano ( 🍕🍝) 👋 Senior

    Architect nodejsdp.link Co-Author of Node.js Design Patterns 👉 @loige 15
  5. Let me introduce myself... I'm Luciano ( 🍕🍝) 👋 Senior

    Architect nodejsdp.link Co-Author of Node.js Design Patterns 👉 Connect with me: (blog) (twitter) (twitch) (github) loige.co @loige loige lmammino @loige 15
  6. We are business focused technologists that deliver. | | Accelerated

    Serverless AI as a Service Platform Modernisation WE ARE HIRING: Do you want to ? work with us @loige 16
  7. Iteration protocols what? why? 🤔 @loige An attempt at standardizing

    "iteration" behaviors providing a consistent and interoperable API Use for...of, for await...of and spread operator. You can also create lazy iterators You can also deal with async iteration You can create your own custom iterators/iterables 17
  8. const judokas = [ 'Driulis Gonzalez Morales', 'Ilias Iliadis', 'Tadahiro

    Nomura', 'Anton Geesink', 'Teddy Riner', 'Ryoko Tani' ] for (const judoka of judokas) { console.log(judoka) } 1 2 3 4 5 6 7 8 9 10 11 12 @loige for...of 19
  9. const judokas = [ 'Driulis Gonzalez Morales', 'Ilias Iliadis', 'Tadahiro

    Nomura', 'Anton Geesink', 'Teddy Riner', 'Ryoko Tani' ] for (const judoka of judokas) { console.log(judoka) } 1 2 3 4 5 6 7 8 9 10 11 12 const judokas = [ 'Driulis Gonzalez Morales', 'Ilias Iliadis', 'Tadahiro Nomura', 'Anton Geesink', 'Teddy Riner', 'Ryoko Tani' ] 1 2 3 4 5 6 7 8 9 for (const judoka of judokas) { 10 console.log(judoka) 11 } 12 @loige for...of Driulis Gonzalez Morales Ilias Iliadis Tadahiro Nomura Anton Geesink Teddy Riner Ryoko Tani OUTPUT 19
  10. const judokas = [ 'Driulis Gonzalez Morales', 'Ilias Iliadis', 'Tadahiro

    Nomura', 'Anton Geesink', 'Teddy Riner', 'Ryoko Tani' ] for (const judoka of judokas) { console.log(judoka) } 1 2 3 4 5 6 7 8 9 10 11 12 const judokas = [ 'Driulis Gonzalez Morales', 'Ilias Iliadis', 'Tadahiro Nomura', 'Anton Geesink', 'Teddy Riner', 'Ryoko Tani' ] 1 2 3 4 5 6 7 8 9 for (const judoka of judokas) { 10 console.log(judoka) 11 } 12 for (const judoka of judokas) { } const judokas = [ 1 'Driulis Gonzalez Morales', 2 'Ilias Iliadis', 3 'Tadahiro Nomura', 4 'Anton Geesink', 5 'Teddy Riner', 6 'Ryoko Tani' 7 ] 8 9 10 console.log(judoka) 11 12 @loige for...of Driulis Gonzalez Morales Ilias Iliadis Tadahiro Nomura Anton Geesink Teddy Riner Ryoko Tani OUTPUT 19
  11. const judokas = [ 'Driulis Gonzalez Morales', 'Ilias Iliadis', 'Tadahiro

    Nomura', 'Anton Geesink', 'Teddy Riner', 'Ryoko Tani' ] for (const judoka of judokas) { console.log(judoka) } 1 2 3 4 5 6 7 8 9 10 11 12 const judokas = [ 'Driulis Gonzalez Morales', 'Ilias Iliadis', 'Tadahiro Nomura', 'Anton Geesink', 'Teddy Riner', 'Ryoko Tani' ] 1 2 3 4 5 6 7 8 9 for (const judoka of judokas) { 10 console.log(judoka) 11 } 12 for (const judoka of judokas) { } const judokas = [ 1 'Driulis Gonzalez Morales', 2 'Ilias Iliadis', 3 'Tadahiro Nomura', 4 'Anton Geesink', 5 'Teddy Riner', 6 'Ryoko Tani' 7 ] 8 9 10 console.log(judoka) 11 12 console.log(judoka) const judokas = [ 1 'Driulis Gonzalez Morales', 2 'Ilias Iliadis', 3 'Tadahiro Nomura', 4 'Anton Geesink', 5 'Teddy Riner', 6 'Ryoko Tani' 7 ] 8 9 for (const judoka of judokas) { 10 11 } 12 @loige for...of Driulis Gonzalez Morales Ilias Iliadis Tadahiro Nomura Anton Geesink Teddy Riner Ryoko Tani OUTPUT 19
  12. const judokas = [ 'Driulis Gonzalez Morales', 'Ilias Iliadis', 'Tadahiro

    Nomura', 'Anton Geesink', 'Teddy Riner', 'Ryoko Tani' ] for (const judoka of judokas) { console.log(judoka) } 1 2 3 4 5 6 7 8 9 10 11 12 const judokas = [ 'Driulis Gonzalez Morales', 'Ilias Iliadis', 'Tadahiro Nomura', 'Anton Geesink', 'Teddy Riner', 'Ryoko Tani' ] 1 2 3 4 5 6 7 8 9 for (const judoka of judokas) { 10 console.log(judoka) 11 } 12 for (const judoka of judokas) { } const judokas = [ 1 'Driulis Gonzalez Morales', 2 'Ilias Iliadis', 3 'Tadahiro Nomura', 4 'Anton Geesink', 5 'Teddy Riner', 6 'Ryoko Tani' 7 ] 8 9 10 console.log(judoka) 11 12 console.log(judoka) const judokas = [ 1 'Driulis Gonzalez Morales', 2 'Ilias Iliadis', 3 'Tadahiro Nomura', 4 'Anton Geesink', 5 'Teddy Riner', 6 'Ryoko Tani' 7 ] 8 9 for (const judoka of judokas) { 10 11 } 12 const judokas = [ 'Driulis Gonzalez Morales', 'Ilias Iliadis', 'Tadahiro Nomura', 'Anton Geesink', 'Teddy Riner', 'Ryoko Tani' ] for (const judoka of judokas) { console.log(judoka) } 1 2 3 4 5 6 7 8 9 10 11 12 @loige for...of Driulis Gonzalez Morales Ilias Iliadis Tadahiro Nomura Anton Geesink Teddy Riner Ryoko Tani OUTPUT 19
  13. const judoka = 'Ryoko Tani' for (const char of judoka)

    { console.log(char) } 1 2 3 4 5 @loige for...of (with strings) 20
  14. const judoka = 'Ryoko Tani' for (const char of judoka)

    { console.log(char) } 1 2 3 4 5 @loige for...of (with strings) R y o k o T a n i OUTPUT 20
  15. const medals = new Set([ 'gold', 'silver', 'bronze' ]) for

    (const medal of medals) { console.log(medal) } 1 2 3 4 5 6 7 8 9 @loige for...of (with Set) 21
  16. const medals = new Set([ 'gold', 'silver', 'bronze' ]) for

    (const medal of medals) { console.log(medal) } 1 2 3 4 5 6 7 8 9 @loige for...of (with Set) gold silver bronze OUTPUT 21
  17. 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`) } 1 2 3 4 5 6 7 8 9 10 @loige for...of (with Map) 22
  18. 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`) } 1 2 3 4 5 6 7 8 9 10 @loige for...of (with Map) 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 OUTPUT 22
  19. 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`) } 1 2 3 4 5 6 7 8 9 10 @loige for...of (with object & Object.entries) 23
  20. 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`) } 1 2 3 4 5 6 7 8 9 10 @loige for...of (with object & Object.entries) 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 OUTPUT 23
  21. 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`) } 1 2 3 4 5 6 7 8 9 10 @loige for...of (with object literals) 24
  22. 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`) } 1 2 3 4 5 6 7 8 9 10 @loige for...of (with object literals) for (const [judoka, medals] of medallists) { ^ TypeError: medallists is not iterable at Object. (.../05-for-of-object.js:8:32) ERROR 24
  23. 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 1 2 3 4 5 6 7 8 9 @loige Spread operator 25
  24. 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 1 2 3 4 5 6 7 8 9 const countdown = [3, 2, 1, 0] 1 2 // spread into array 3 const from5to0 = [5, 4, ...countdown] 4 console.log(from5to0) // [ 5, 4, 3, 2, 1, 0 ] 5 6 // spread function arguments 7 console.log('countdown data:', ...countdown) 8 // countdown data: 3 2 1 0 9 @loige Spread operator 25
  25. 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 1 2 3 4 5 6 7 8 9 const countdown = [3, 2, 1, 0] 1 2 // spread into array 3 const from5to0 = [5, 4, ...countdown] 4 console.log(from5to0) // [ 5, 4, 3, 2, 1, 0 ] 5 6 // spread function arguments 7 console.log('countdown data:', ...countdown) 8 // countdown data: 3 2 1 0 9 // spread into array const from5to0 = [5, 4, ...countdown] console.log(from5to0) // [ 5, 4, 3, 2, 1, 0 ] const countdown = [3, 2, 1, 0] 1 2 3 4 5 6 // spread function arguments 7 console.log('countdown data:', ...countdown) 8 // countdown data: 3 2 1 0 9 @loige Spread operator 25
  26. 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 1 2 3 4 5 6 7 8 9 const countdown = [3, 2, 1, 0] 1 2 // spread into array 3 const from5to0 = [5, 4, ...countdown] 4 console.log(from5to0) // [ 5, 4, 3, 2, 1, 0 ] 5 6 // spread function arguments 7 console.log('countdown data:', ...countdown) 8 // countdown data: 3 2 1 0 9 // spread into array const from5to0 = [5, 4, ...countdown] console.log(from5to0) // [ 5, 4, 3, 2, 1, 0 ] const countdown = [3, 2, 1, 0] 1 2 3 4 5 6 // spread function arguments 7 console.log('countdown data:', ...countdown) 8 // countdown data: 3 2 1 0 9 // spread function arguments console.log('countdown data:', ...countdown) // countdown data: 3 2 1 0 const countdown = [3, 2, 1, 0] 1 2 // spread into array 3 const from5to0 = [5, 4, ...countdown] 4 console.log(from5to0) // [ 5, 4, 3, 2, 1, 0 ] 5 6 7 8 9 @loige Spread operator 25
  27. 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 1 2 3 4 5 6 7 8 9 const countdown = [3, 2, 1, 0] 1 2 // spread into array 3 const from5to0 = [5, 4, ...countdown] 4 console.log(from5to0) // [ 5, 4, 3, 2, 1, 0 ] 5 6 // spread function arguments 7 console.log('countdown data:', ...countdown) 8 // countdown data: 3 2 1 0 9 // spread into array const from5to0 = [5, 4, ...countdown] console.log(from5to0) // [ 5, 4, 3, 2, 1, 0 ] const countdown = [3, 2, 1, 0] 1 2 3 4 5 6 // spread function arguments 7 console.log('countdown data:', ...countdown) 8 // countdown data: 3 2 1 0 9 // spread function arguments console.log('countdown data:', ...countdown) // countdown data: 3 2 1 0 const countdown = [3, 2, 1, 0] 1 2 // spread into array 3 const from5to0 = [5, 4, ...countdown] 4 console.log(from5to0) // [ 5, 4, 3, 2, 1, 0 ] 5 6 7 8 9 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 1 2 3 4 5 6 7 8 9 @loige Spread operator 25
  28. 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) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 @loige for await...of (async iterable) 26
  29. 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) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 import { DynamoDBClient, paginateListTables } from '@aws-sdk/client-dynamodb' 1 2 3 4 5 const client = new DynamoDBClient({}); 6 7 for await (const page of paginateListTables({ client }, {})) { 8 // page.TableNames is an array of table names 9 for (const tableName of page.TableNames) { 10 console.log(tableName) 11 } 12 } 13 @loige for await...of (async iterable) 26
  30. 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) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 import { DynamoDBClient, paginateListTables } from '@aws-sdk/client-dynamodb' 1 2 3 4 5 const client = new DynamoDBClient({}); 6 7 for await (const page of paginateListTables({ client }, {})) { 8 // page.TableNames is an array of table names 9 for (const tableName of page.TableNames) { 10 console.log(tableName) 11 } 12 } 13 const client = new DynamoDBClient({}); import { 1 DynamoDBClient, 2 paginateListTables 3 } from '@aws-sdk/client-dynamodb' 4 5 6 7 for await (const page of paginateListTables({ client }, {})) { 8 // page.TableNames is an array of table names 9 for (const tableName of page.TableNames) { 10 console.log(tableName) 11 } 12 } 13 @loige for await...of (async iterable) 26
  31. 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) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 import { DynamoDBClient, paginateListTables } from '@aws-sdk/client-dynamodb' 1 2 3 4 5 const client = new DynamoDBClient({}); 6 7 for await (const page of paginateListTables({ client }, {})) { 8 // page.TableNames is an array of table names 9 for (const tableName of page.TableNames) { 10 console.log(tableName) 11 } 12 } 13 const client = new DynamoDBClient({}); import { 1 DynamoDBClient, 2 paginateListTables 3 } from '@aws-sdk/client-dynamodb' 4 5 6 7 for await (const page of paginateListTables({ client }, {})) { 8 // page.TableNames is an array of table names 9 for (const tableName of page.TableNames) { 10 console.log(tableName) 11 } 12 } 13 for await (const page of paginateListTables({ client }, {})) { } import { 1 DynamoDBClient, 2 paginateListTables 3 } from '@aws-sdk/client-dynamodb' 4 5 const client = new DynamoDBClient({}); 6 7 8 // page.TableNames is an array of table names 9 for (const tableName of page.TableNames) { 10 console.log(tableName) 11 } 12 13 @loige for await...of (async iterable) 26
  32. 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) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 import { DynamoDBClient, paginateListTables } from '@aws-sdk/client-dynamodb' 1 2 3 4 5 const client = new DynamoDBClient({}); 6 7 for await (const page of paginateListTables({ client }, {})) { 8 // page.TableNames is an array of table names 9 for (const tableName of page.TableNames) { 10 console.log(tableName) 11 } 12 } 13 const client = new DynamoDBClient({}); import { 1 DynamoDBClient, 2 paginateListTables 3 } from '@aws-sdk/client-dynamodb' 4 5 6 7 for await (const page of paginateListTables({ client }, {})) { 8 // page.TableNames is an array of table names 9 for (const tableName of page.TableNames) { 10 console.log(tableName) 11 } 12 } 13 for await (const page of paginateListTables({ client }, {})) { } import { 1 DynamoDBClient, 2 paginateListTables 3 } from '@aws-sdk/client-dynamodb' 4 5 const client = new DynamoDBClient({}); 6 7 8 // page.TableNames is an array of table names 9 for (const tableName of page.TableNames) { 10 console.log(tableName) 11 } 12 13 // page.TableNames is an array of table names for (const tableName of page.TableNames) { console.log(tableName) } import { 1 DynamoDBClient, 2 paginateListTables 3 } from '@aws-sdk/client-dynamodb' 4 5 const client = new DynamoDBClient({}); 6 7 for await (const page of paginateListTables({ client }, {})) { 8 9 10 11 12 } 13 @loige for await...of (async iterable) 26
  33. 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) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 import { DynamoDBClient, paginateListTables } from '@aws-sdk/client-dynamodb' 1 2 3 4 5 const client = new DynamoDBClient({}); 6 7 for await (const page of paginateListTables({ client }, {})) { 8 // page.TableNames is an array of table names 9 for (const tableName of page.TableNames) { 10 console.log(tableName) 11 } 12 } 13 const client = new DynamoDBClient({}); import { 1 DynamoDBClient, 2 paginateListTables 3 } from '@aws-sdk/client-dynamodb' 4 5 6 7 for await (const page of paginateListTables({ client }, {})) { 8 // page.TableNames is an array of table names 9 for (const tableName of page.TableNames) { 10 console.log(tableName) 11 } 12 } 13 for await (const page of paginateListTables({ client }, {})) { } import { 1 DynamoDBClient, 2 paginateListTables 3 } from '@aws-sdk/client-dynamodb' 4 5 const client = new DynamoDBClient({}); 6 7 8 // page.TableNames is an array of table names 9 for (const tableName of page.TableNames) { 10 console.log(tableName) 11 } 12 13 // page.TableNames is an array of table names for (const tableName of page.TableNames) { console.log(tableName) } import { 1 DynamoDBClient, 2 paginateListTables 3 } from '@aws-sdk/client-dynamodb' 4 5 const client = new DynamoDBClient({}); 6 7 for await (const page of paginateListTables({ client }, {})) { 8 9 10 11 12 } 13 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) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 @loige for await...of (async iterable) 26
  34. 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 28
  35. function createCountdown (start) { let nextVal = start return {

    next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @loige Countdown iterator 29
  36. function createCountdown (start) { let nextVal = start return {

    next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 14 @loige Countdown 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 } 29
  37. function createCountdown (start) { let nextVal = start return {

    next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 14 let nextVal = start function createCountdown (start) { 1 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 } 14 @loige Countdown 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 } 29
  38. function createCountdown (start) { let nextVal = start return {

    next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 14 let nextVal = start function createCountdown (start) { 1 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 } 14 return { } function createCountdown (start) { 1 let nextVal = start 2 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 13 } 14 @loige Countdown 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 } 29
  39. function createCountdown (start) { let nextVal = start return {

    next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 14 let nextVal = start function createCountdown (start) { 1 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 } 14 return { } function createCountdown (start) { 1 let nextVal = start 2 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 13 } 14 next () { } function createCountdown (start) { 1 let nextVal = start 2 return { 3 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 12 } 13 } 14 @loige Countdown 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 } 29
  40. function createCountdown (start) { let nextVal = start return {

    next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 14 let nextVal = start function createCountdown (start) { 1 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 } 14 return { } function createCountdown (start) { 1 let nextVal = start 2 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 13 } 14 next () { } function createCountdown (start) { 1 let nextVal = start 2 return { 3 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 12 } 13 } 14 if (nextVal < 0) { return { done: true } } function createCountdown (start) { 1 let nextVal = start 2 return { 3 next () { 4 5 6 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 } 14 @loige Countdown 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 } 29
  41. function createCountdown (start) { let nextVal = start return {

    next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 14 let nextVal = start function createCountdown (start) { 1 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 } 14 return { } function createCountdown (start) { 1 let nextVal = start 2 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 13 } 14 next () { } function createCountdown (start) { 1 let nextVal = start 2 return { 3 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 12 } 13 } 14 if (nextVal < 0) { return { done: true } } function createCountdown (start) { 1 let nextVal = start 2 return { 3 next () { 4 5 6 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 } 14 return { done: false, value: nextVal-- } function createCountdown (start) { 1 let nextVal = start 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 8 9 10 11 } 12 } 13 } 14 @loige Countdown 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 } 29
  42. function createCountdown (start) { let nextVal = start return {

    next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 14 let nextVal = start function createCountdown (start) { 1 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 } 14 return { } function createCountdown (start) { 1 let nextVal = start 2 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 13 } 14 next () { } function createCountdown (start) { 1 let nextVal = start 2 return { 3 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 12 } 13 } 14 if (nextVal < 0) { return { done: true } } function createCountdown (start) { 1 let nextVal = start 2 return { 3 next () { 4 5 6 7 return { 8 done: false, 9 value: nextVal-- 10 } 11 } 12 } 13 } 14 return { done: false, value: nextVal-- } function createCountdown (start) { 1 let nextVal = start 2 return { 3 next () { 4 if (nextVal < 0) { 5 return { done: true } 6 } 7 8 9 10 11 } 12 } 13 } 14 function createCountdown (start) { let nextVal = start return { next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @loige Countdown 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 } 29
  43. Iterable protocol An object is iterable if it implements the

    @@iterator* method, a zero-argument function that returns an iterator. * Symbol.iterator @loige 30
  44. function createCountdown (start) { let nextVal = start return {

    [Symbol.iterator]: () => ({ next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } }) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @loige Countdown iterable 31
  45. function createCountdown (start) { let nextVal = start return {

    [Symbol.iterator]: () => ({ next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } }) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 } 15 16 @loige Countdown iterable const countdown = createCountdown(3) for (const value of countdown) { console.log(value) } // 3 // 2 // 1 // 0 31
  46. function createCountdown (start) { let nextVal = start return {

    [Symbol.iterator]: () => ({ next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } }) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 } 15 16 let nextVal = start function createCountdown (start) { 1 2 return { 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 } 15 } 16 @loige Countdown iterable const countdown = createCountdown(3) for (const value of countdown) { console.log(value) } // 3 // 2 // 1 // 0 31
  47. function createCountdown (start) { let nextVal = start return {

    [Symbol.iterator]: () => ({ next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } }) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 } 15 16 let nextVal = start function createCountdown (start) { 1 2 return { 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 } 15 } 16 return { } function createCountdown (start) { 1 let nextVal = start 2 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 15 } 16 @loige Countdown iterable const countdown = createCountdown(3) for (const value of countdown) { console.log(value) } // 3 // 2 // 1 // 0 31
  48. function createCountdown (start) { let nextVal = start return {

    [Symbol.iterator]: () => ({ next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } }) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 } 15 16 let nextVal = start function createCountdown (start) { 1 2 return { 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 } 15 } 16 return { } function createCountdown (start) { 1 let nextVal = start 2 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 15 } 16 [Symbol.iterator]: () => ({ }) function createCountdown (start) { 1 let nextVal = start 2 return { 3 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 14 } 15 } 16 @loige Countdown iterable const countdown = createCountdown(3) for (const value of countdown) { console.log(value) } // 3 // 2 // 1 // 0 31
  49. function createCountdown (start) { let nextVal = start return {

    [Symbol.iterator]: () => ({ next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } }) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 } 15 16 let nextVal = start function createCountdown (start) { 1 2 return { 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 } 15 } 16 return { } function createCountdown (start) { 1 let nextVal = start 2 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 15 } 16 [Symbol.iterator]: () => ({ }) function createCountdown (start) { 1 let nextVal = start 2 return { 3 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 14 } 15 } 16 next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } function createCountdown (start) { 1 let nextVal = start 2 return { 3 [Symbol.iterator]: () => ({ 4 5 6 7 8 9 10 11 12 13 }) 14 } 15 } 16 @loige Countdown iterable const countdown = createCountdown(3) for (const value of countdown) { console.log(value) } // 3 // 2 // 1 // 0 31
  50. function createCountdown (start) { let nextVal = start return {

    [Symbol.iterator]: () => ({ next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } }) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function createCountdown (start) { } 1 let nextVal = start 2 return { 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 } 15 16 let nextVal = start function createCountdown (start) { 1 2 return { 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 } 15 } 16 return { } function createCountdown (start) { 1 let nextVal = start 2 3 [Symbol.iterator]: () => ({ 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 }) 14 15 } 16 [Symbol.iterator]: () => ({ }) function createCountdown (start) { 1 let nextVal = start 2 return { 3 4 next () { 5 if (nextVal < 0) { 6 return { done: true } 7 } 8 return { 9 done: false, 10 value: nextVal-- 11 } 12 } 13 14 } 15 } 16 next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } function createCountdown (start) { 1 let nextVal = start 2 return { 3 [Symbol.iterator]: () => ({ 4 5 6 7 8 9 10 11 12 13 }) 14 } 15 } 16 function createCountdown (start) { let nextVal = start return { [Symbol.iterator]: () => ({ next () { if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } }) } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @loige Countdown iterable const countdown = createCountdown(3) for (const value of countdown) { console.log(value) } // 3 // 2 // 1 // 0 31
  51. const iterableIterator = { next() { return { done: false,

    value: "hello" } }, [Symbol.iterator]() { return this } } 1 2 3 4 5 6 7 8 @loige Iterator + Iterable 33
  52. const iterableIterator = { next() { return { done: false,

    value: "hello" } }, [Symbol.iterator]() { return this } } 1 2 3 4 5 6 7 8 const iterableIterator = { } 1 next() { 2 return { done: false, value: "hello" } 3 }, 4 [Symbol.iterator]() { 5 return this 6 } 7 8 @loige Iterator + Iterable iterableIterator.next() // { done: false, value: "hello" } for (const value of iterableIterator) { console.log(value) } // hello // hello // hello // ... 33
  53. const iterableIterator = { next() { return { done: false,

    value: "hello" } }, [Symbol.iterator]() { return this } } 1 2 3 4 5 6 7 8 const iterableIterator = { } 1 next() { 2 return { done: false, value: "hello" } 3 }, 4 [Symbol.iterator]() { 5 return this 6 } 7 8 next() { return { done: false, value: "hello" } }, const iterableIterator = { 1 2 3 4 [Symbol.iterator]() { 5 return this 6 } 7 } 8 @loige Iterator + Iterable iterableIterator.next() // { done: false, value: "hello" } for (const value of iterableIterator) { console.log(value) } // hello // hello // hello // ... 33
  54. const iterableIterator = { next() { return { done: false,

    value: "hello" } }, [Symbol.iterator]() { return this } } 1 2 3 4 5 6 7 8 const iterableIterator = { } 1 next() { 2 return { done: false, value: "hello" } 3 }, 4 [Symbol.iterator]() { 5 return this 6 } 7 8 next() { return { done: false, value: "hello" } }, const iterableIterator = { 1 2 3 4 [Symbol.iterator]() { 5 return this 6 } 7 } 8 [Symbol.iterator]() { return this } const iterableIterator = { 1 next() { 2 return { done: false, value: "hello" } 3 }, 4 5 6 7 } 8 @loige Iterator + Iterable iterableIterator.next() // { done: false, value: "hello" } for (const value of iterableIterator) { console.log(value) } // hello // hello // hello // ... 33
  55. const iterableIterator = { next() { return { done: false,

    value: "hello" } }, [Symbol.iterator]() { return this } } 1 2 3 4 5 6 7 8 const iterableIterator = { } 1 next() { 2 return { done: false, value: "hello" } 3 }, 4 [Symbol.iterator]() { 5 return this 6 } 7 8 next() { return { done: false, value: "hello" } }, const iterableIterator = { 1 2 3 4 [Symbol.iterator]() { 5 return this 6 } 7 } 8 [Symbol.iterator]() { return this } const iterableIterator = { 1 next() { 2 return { done: false, value: "hello" } 3 }, 4 5 6 7 } 8 const iterableIterator = { next() { return { done: false, value: "hello" } }, [Symbol.iterator]() { return this } } 1 2 3 4 5 6 7 8 @loige Iterator + Iterable iterableIterator.next() // { done: false, value: "hello" } for (const value of iterableIterator) { console.log(value) } // hello // hello // hello // ... 33
  56. Generators A generator function "produces" an object that is both

    an iterator and an iterable! 🤯 @loige 34
  57. function * createCountdown (start) { for (let i = start;

    i >= 0; i--) { yield i } } 1 2 3 4 5 @loige 35
  58. function * createCountdown (start) { for (let i = start;

    i >= 0; i--) { yield i } } 1 2 3 4 5 function * createCountdown (start) { } 1 for (let i = start; i >= 0; i--) { 2 yield i 3 } 4 5 @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 } 35
  59. function * createCountdown (start) { for (let i = start;

    i >= 0; i--) { yield i } } 1 2 3 4 5 function * createCountdown (start) { } 1 for (let i = start; i >= 0; i--) { 2 yield i 3 } 4 5 for (let i = start; i >= 0; i--) { } function * createCountdown (start) { 1 2 yield i 3 4 } 5 @loige // As iterable const countdown = createCountdown(3) for (const value of countdown) { console.log(value) } // 3 // 2 // 1 // 0 // 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 } 35
  60. function * createCountdown (start) { for (let i = start;

    i >= 0; i--) { yield i } } 1 2 3 4 5 function * createCountdown (start) { } 1 for (let i = start; i >= 0; i--) { 2 yield i 3 } 4 5 for (let i = start; i >= 0; i--) { } function * createCountdown (start) { 1 2 yield i 3 4 } 5 yield i function * createCountdown (start) { 1 for (let i = start; i >= 0; i--) { 2 3 } 4 } 5 @loige // As iterable const countdown = createCountdown(3) for (const value of countdown) { console.log(value) } // 3 // 2 // 1 // 0 // 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 } 35
  61. function * createCountdown (start) { for (let i = start;

    i >= 0; i--) { yield i } } 1 2 3 4 5 function * createCountdown (start) { } 1 for (let i = start; i >= 0; i--) { 2 yield i 3 } 4 5 for (let i = start; i >= 0; i--) { } function * createCountdown (start) { 1 2 yield i 3 4 } 5 yield i function * createCountdown (start) { 1 for (let i = start; i >= 0; i--) { 2 3 } 4 } 5 function * createCountdown (start) { for (let i = start; i >= 0; i--) { yield i } } 1 2 3 4 5 @loige // As iterable const countdown = createCountdown(3) for (const value of countdown) { console.log(value) } // 3 // 2 // 1 // 0 // 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 } 35
  62. 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 37
  63. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @loige 38
  64. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 @loige 38
  65. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 14 @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 } 38
  66. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 14 let nextVal = start import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 @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 } 38
  67. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 14 let nextVal = start import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 return { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 13 } 14 @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 } 38
  68. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 14 let nextVal = start import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 return { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 13 } 14 async next () { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 12 } 13 } 14 @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 } 38
  69. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 14 let nextVal = start import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 return { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 13 } 14 async next () { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 12 } 13 } 14 await setTimeout(delay) import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 @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 } 38
  70. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 14 let nextVal = start import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 return { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 13 } 14 async next () { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 12 } 13 } 14 await setTimeout(delay) import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 8 9 10 11 } 12 } 13 } 14 @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 } 38
  71. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 14 let nextVal = start import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 return { 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 return { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 5 async next () { 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 13 } 14 async next () { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 6 await setTimeout(delay) 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 12 } 13 } 14 await setTimeout(delay) import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 7 if (nextVal < 0) { 8 return { done: true } 9 } 10 return { done: false, value: nextVal-- } 11 } 12 } 13 } 14 if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 let nextVal = start 4 return { 5 async next () { 6 await setTimeout(delay) 7 8 9 10 11 } 12 } 13 } 14 import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay = 1000) { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @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 } 38
  72. 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 40
  73. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { return { [Symbol.asyncIterator]: function () { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @loige 41
  74. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { return { [Symbol.asyncIterator]: function () { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 17 } 18 @loige 41
  75. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { return { [Symbol.asyncIterator]: function () { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 17 } 18 return { import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 } 17 } 18 @loige const countdown = createAsyncCountdown(3) for await (const value of countdown) { console.log(value) // 3 ... 2 ... 1 ... 0 } 41
  76. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { return { [Symbol.asyncIterator]: function () { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 17 } 18 return { import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 } 17 } 18 [Symbol.asyncIterator]: function () { return { done: false, value: nextVal-- } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 13 } 14 } 15 } 16 } 17 } 18 @loige const countdown = createAsyncCountdown(3) for await (const value of countdown) { console.log(value) // 3 ... 2 ... 1 ... 0 } 41
  77. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { return { [Symbol.asyncIterator]: function () { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 17 } 18 return { import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 } 17 } 18 [Symbol.asyncIterator]: function () { return { done: false, value: nextVal-- } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 13 } 14 } 15 } 16 } 17 } 18 let nextVal = start import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 } 17 } 18 @loige const countdown = createAsyncCountdown(3) for await (const value of countdown) { console.log(value) // 3 ... 2 ... 1 ... 0 } 41
  78. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { return { [Symbol.asyncIterator]: function () { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 17 } 18 return { import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 } 17 } 18 [Symbol.asyncIterator]: function () { return { done: false, value: nextVal-- } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 13 } 14 } 15 } 16 } 17 } 18 let nextVal = start import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 } 17 } 18 return { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 15 } 16 } 17 } 18 @loige const countdown = createAsyncCountdown(3) for await (const value of countdown) { console.log(value) // 3 ... 2 ... 1 ... 0 } 41
  79. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { return { [Symbol.asyncIterator]: function () { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 17 } 18 return { import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 } 17 } 18 [Symbol.asyncIterator]: function () { return { done: false, value: nextVal-- } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 13 } 14 } 15 } 16 } 17 } 18 let nextVal = start import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 } 17 } 18 return { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 15 } 16 } 17 } 18 async next () { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 14 } 15 } 16 } 17 } 18 @loige const countdown = createAsyncCountdown(3) for await (const value of countdown) { console.log(value) // 3 ... 2 ... 1 ... 0 } 41
  80. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { return { [Symbol.asyncIterator]: function () { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 17 } 18 return { import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 } 17 } 18 [Symbol.asyncIterator]: function () { return { done: false, value: nextVal-- } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 13 } 14 } 15 } 16 } 17 } 18 let nextVal = start import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 } 17 } 18 return { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 15 } 16 } 17 } 18 async next () { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 14 } 15 } 16 } 17 } 18 await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 9 10 11 12 13 } 14 } 15 } 16 } 17 } 18 @loige const countdown = createAsyncCountdown(3) for await (const value of countdown) { console.log(value) // 3 ... 2 ... 1 ... 0 } 41
  81. import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay

    = 1000) { return { [Symbol.asyncIterator]: function () { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 17 } 18 return { import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 } 17 } 18 [Symbol.asyncIterator]: function () { return { done: false, value: nextVal-- } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 5 let nextVal = start 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 13 } 14 } 15 } 16 } 17 } 18 let nextVal = start import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 6 return { 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 } 15 } 16 } 17 } 18 return { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 7 async next () { 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 } 14 15 } 16 } 17 } 18 async next () { } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 8 await setTimeout(delay) 9 if (nextVal < 0) { 10 return { done: true } 11 } 12 return { done: false, value: nextVal-- } 13 14 } 15 } 16 } 17 } 18 await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } import { setTimeout } from 'timers/promises' 1 2 function createAsyncCountdown (start, delay = 1000) { 3 return { 4 [Symbol.asyncIterator]: function () { 5 let nextVal = start 6 return { 7 async next () { 8 9 10 11 12 13 } 14 } 15 } 16 } 17 } 18 import { setTimeout } from 'timers/promises' function createAsyncCountdown (start, delay = 1000) { return { [Symbol.asyncIterator]: function () { let nextVal = start return { async next () { await setTimeout(delay) if (nextVal < 0) { return { done: true } } return { done: false, value: nextVal-- } } } } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @loige const countdown = createAsyncCountdown(3) for await (const value of countdown) { console.log(value) // 3 ... 2 ... 1 ... 0 } 41
  82. import { setTimeout } from 'timers/promises' async function * createAsyncCountdown

    (start, delay = 1000) { for (let i = start; i >= 0; i--) { await setTimeout(delay) yield i } } 1 2 3 4 5 6 7 8 @loige 42
  83. import { setTimeout } from 'timers/promises' async function * createAsyncCountdown

    (start, delay = 1000) { for (let i = start; i >= 0; i--) { await setTimeout(delay) yield i } } 1 2 3 4 5 6 7 8 async function * createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 for (let i = start; i >= 0; i--) { 4 await setTimeout(delay) 5 yield i 6 } 7 8 @loige const countdown = createAsyncCountdown(3) for await (const value of countdown) { console.log(value) // 3 ... 2 ... 1 ... 0 } 42
  84. import { setTimeout } from 'timers/promises' async function * createAsyncCountdown

    (start, delay = 1000) { for (let i = start; i >= 0; i--) { await setTimeout(delay) yield i } } 1 2 3 4 5 6 7 8 async function * createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 for (let i = start; i >= 0; i--) { 4 await setTimeout(delay) 5 yield i 6 } 7 8 await setTimeout(delay) yield i import { setTimeout } from 'timers/promises' 1 2 async function * createAsyncCountdown (start, delay = 1000) { 3 for (let i = start; i >= 0; i--) { 4 5 6 } 7 } 8 @loige const countdown = createAsyncCountdown(3) for await (const value of countdown) { console.log(value) // 3 ... 2 ... 1 ... 0 } 42
  85. import { setTimeout } from 'timers/promises' async function * createAsyncCountdown

    (start, delay = 1000) { for (let i = start; i >= 0; i--) { await setTimeout(delay) yield i } } 1 2 3 4 5 6 7 8 async function * createAsyncCountdown (start, delay = 1000) { } import { setTimeout } from 'timers/promises' 1 2 3 for (let i = start; i >= 0; i--) { 4 await setTimeout(delay) 5 yield i 6 } 7 8 await setTimeout(delay) yield i import { setTimeout } from 'timers/promises' 1 2 async function * createAsyncCountdown (start, delay = 1000) { 3 for (let i = start; i >= 0; i--) { 4 5 6 } 7 } 8 import { setTimeout } from 'timers/promises' async function * createAsyncCountdown (start, delay = 1000) { for (let i = start; i >= 0; i--) { await setTimeout(delay) yield i } } 1 2 3 4 5 6 7 8 @loige const countdown = createAsyncCountdown(3) for await (const value of countdown) { console.log(value) // 3 ... 2 ... 1 ... 0 } 42
  86. When to use async iterators @loige Sequential iteration pattern Data

    arriving in order over time You need to complete processing the current “chunk” before you can request the next one Examples paginated iteration consuming tasks from a remote queue 44
  87. import { createReadStream } from 'fs' const sourceStream = createReadStream('bigdata.csv')

    let bytes = 0 for await (const chunk of sourceStream) { bytes += chunk.length } console.log(`bigdata.csv: ${bytes} bytes`) 1 2 3 4 5 6 7 8 9 10 @loige Node.js readable streams are async iterators 46
  88. import { createReadStream } from 'fs' const sourceStream = createReadStream('bigdata.csv')

    let bytes = 0 for await (const chunk of sourceStream) { bytes += chunk.length } console.log(`bigdata.csv: ${bytes} bytes`) 1 2 3 4 5 6 7 8 9 10 import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 @loige Node.js readable streams are async iterators 46
  89. import { createReadStream } from 'fs' const sourceStream = createReadStream('bigdata.csv')

    let bytes = 0 for await (const chunk of sourceStream) { bytes += chunk.length } console.log(`bigdata.csv: ${bytes} bytes`) 1 2 3 4 5 6 7 8 9 10 import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 2 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 @loige Node.js readable streams are async iterators 46
  90. import { createReadStream } from 'fs' const sourceStream = createReadStream('bigdata.csv')

    let bytes = 0 for await (const chunk of sourceStream) { bytes += chunk.length } console.log(`bigdata.csv: ${bytes} bytes`) 1 2 3 4 5 6 7 8 9 10 import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 2 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 let bytes = 0 import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 @loige Node.js readable streams are async iterators 46
  91. import { createReadStream } from 'fs' const sourceStream = createReadStream('bigdata.csv')

    let bytes = 0 for await (const chunk of sourceStream) { bytes += chunk.length } console.log(`bigdata.csv: ${bytes} bytes`) 1 2 3 4 5 6 7 8 9 10 import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 2 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 let bytes = 0 import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 for await (const chunk of sourceStream) { } import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 6 bytes += chunk.length 7 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 @loige Node.js readable streams are async iterators 46
  92. import { createReadStream } from 'fs' const sourceStream = createReadStream('bigdata.csv')

    let bytes = 0 for await (const chunk of sourceStream) { bytes += chunk.length } console.log(`bigdata.csv: ${bytes} bytes`) 1 2 3 4 5 6 7 8 9 10 import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 2 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 let bytes = 0 import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 for await (const chunk of sourceStream) { } import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 6 bytes += chunk.length 7 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 bytes += chunk.length import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 @loige Node.js readable streams are async iterators 46
  93. import { createReadStream } from 'fs' const sourceStream = createReadStream('bigdata.csv')

    let bytes = 0 for await (const chunk of sourceStream) { bytes += chunk.length } console.log(`bigdata.csv: ${bytes} bytes`) 1 2 3 4 5 6 7 8 9 10 import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 2 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 let bytes = 0 import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 for await (const chunk of sourceStream) { } import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 6 bytes += chunk.length 7 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 bytes += chunk.length import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 console.log(`bigdata.csv: ${bytes} bytes`) import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 10 @loige Node.js readable streams are async iterators 46
  94. import { createReadStream } from 'fs' const sourceStream = createReadStream('bigdata.csv')

    let bytes = 0 for await (const chunk of sourceStream) { bytes += chunk.length } console.log(`bigdata.csv: ${bytes} bytes`) 1 2 3 4 5 6 7 8 9 10 import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 2 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 let bytes = 0 import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 for await (const chunk of sourceStream) { } import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 6 bytes += chunk.length 7 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 bytes += chunk.length import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 7 } 8 9 console.log(`bigdata.csv: ${bytes} bytes`) 10 console.log(`bigdata.csv: ${bytes} bytes`) import { createReadStream } from 'fs' 1 2 const sourceStream = createReadStream('bigdata.csv') 3 4 let bytes = 0 5 for await (const chunk of sourceStream) { 6 bytes += chunk.length 7 } 8 9 10 import { createReadStream } from 'fs' const sourceStream = createReadStream('bigdata.csv') let bytes = 0 for await (const chunk of sourceStream) { bytes += chunk.length } console.log(`bigdata.csv: ${bytes} bytes`) 1 2 3 4 5 6 7 8 9 10 @loige Node.js readable streams are async iterators 46
  95. import { createReadStream } from 'fs' import { once }

    from 'events' const sourceStream = createReadStream('bigdata.csv') const destStream = new SlowTransform() for await (const chunk of sourceStream) { const canContinue = destStream.write(chunk) if (!canContinue) { // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') // ok now it's safe to resume writing } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @loige 48
  96. import { createReadStream } from 'fs' import { once }

    from 'events' const sourceStream = createReadStream('bigdata.csv') const destStream = new SlowTransform() for await (const chunk of sourceStream) { const canContinue = destStream.write(chunk) if (!canContinue) { // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') // ok now it's safe to resume writing } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createReadStream } from 'fs' import { once } from 'events' 1 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 @loige 48
  97. import { createReadStream } from 'fs' import { once }

    from 'events' const sourceStream = createReadStream('bigdata.csv') const destStream = new SlowTransform() for await (const chunk of sourceStream) { const canContinue = destStream.write(chunk) if (!canContinue) { // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') // ok now it's safe to resume writing } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createReadStream } from 'fs' import { once } from 'events' 1 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 @loige 48
  98. import { createReadStream } from 'fs' import { once }

    from 'events' const sourceStream = createReadStream('bigdata.csv') const destStream = new SlowTransform() for await (const chunk of sourceStream) { const canContinue = destStream.write(chunk) if (!canContinue) { // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') // ok now it's safe to resume writing } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createReadStream } from 'fs' import { once } from 'events' 1 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const destStream = new SlowTransform() import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 @loige 48
  99. import { createReadStream } from 'fs' import { once }

    from 'events' const sourceStream = createReadStream('bigdata.csv') const destStream = new SlowTransform() for await (const chunk of sourceStream) { const canContinue = destStream.write(chunk) if (!canContinue) { // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') // ok now it's safe to resume writing } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createReadStream } from 'fs' import { once } from 'events' 1 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const destStream = new SlowTransform() import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 for await (const chunk of sourceStream) { } import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 14 @loige 48
  100. import { createReadStream } from 'fs' import { once }

    from 'events' const sourceStream = createReadStream('bigdata.csv') const destStream = new SlowTransform() for await (const chunk of sourceStream) { const canContinue = destStream.write(chunk) if (!canContinue) { // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') // ok now it's safe to resume writing } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createReadStream } from 'fs' import { once } from 'events' 1 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const destStream = new SlowTransform() import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 for await (const chunk of sourceStream) { } import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 14 const canContinue = destStream.write(chunk) import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 @loige 48
  101. import { createReadStream } from 'fs' import { once }

    from 'events' const sourceStream = createReadStream('bigdata.csv') const destStream = new SlowTransform() for await (const chunk of sourceStream) { const canContinue = destStream.write(chunk) if (!canContinue) { // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') // ok now it's safe to resume writing } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createReadStream } from 'fs' import { once } from 'events' 1 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const destStream = new SlowTransform() import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 for await (const chunk of sourceStream) { } import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 14 const canContinue = destStream.write(chunk) import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 if (!canContinue) { } import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 13 } 14 @loige 48
  102. import { createReadStream } from 'fs' import { once }

    from 'events' const sourceStream = createReadStream('bigdata.csv') const destStream = new SlowTransform() for await (const chunk of sourceStream) { const canContinue = destStream.write(chunk) if (!canContinue) { // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') // ok now it's safe to resume writing } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createReadStream } from 'fs' import { once } from 'events' 1 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const destStream = new SlowTransform() import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 for await (const chunk of sourceStream) { } import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 14 const canContinue = destStream.write(chunk) import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 if (!canContinue) { } import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 13 } 14 // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 10 11 // ok now it's safe to resume writing 12 } 13 } 14 @loige 48
  103. import { createReadStream } from 'fs' import { once }

    from 'events' const sourceStream = createReadStream('bigdata.csv') const destStream = new SlowTransform() for await (const chunk of sourceStream) { const canContinue = destStream.write(chunk) if (!canContinue) { // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') // ok now it's safe to resume writing } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createReadStream } from 'fs' import { once } from 'events' 1 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const destStream = new SlowTransform() import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 for await (const chunk of sourceStream) { } import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 14 const canContinue = destStream.write(chunk) import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 if (!canContinue) { } import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 13 } 14 // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 10 11 // ok now it's safe to resume writing 12 } 13 } 14 // ok now it's safe to resume writing import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 12 } 13 } 14 @loige 48
  104. import { createReadStream } from 'fs' import { once }

    from 'events' const sourceStream = createReadStream('bigdata.csv') const destStream = new SlowTransform() for await (const chunk of sourceStream) { const canContinue = destStream.write(chunk) if (!canContinue) { // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') // ok now it's safe to resume writing } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import { createReadStream } from 'fs' import { once } from 'events' 1 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const sourceStream = createReadStream('bigdata.csv') import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 const destStream = new SlowTransform() import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 for await (const chunk of sourceStream) { } import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 14 const canContinue = destStream.write(chunk) import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 } 13 } 14 if (!canContinue) { } import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 // ok now it's safe to resume writing 12 13 } 14 // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 10 11 // ok now it's safe to resume writing 12 } 13 } 14 // ok now it's safe to resume writing import { createReadStream } from 'fs' 1 import { once } from 'events' 2 3 const sourceStream = createReadStream('bigdata.csv') 4 const destStream = new SlowTransform() 5 6 for await (const chunk of sourceStream) { 7 const canContinue = destStream.write(chunk) 8 if (!canContinue) { 9 // backpressure, now we stop and we need to wait for drain 10 await once(destStream, 'drain') 11 12 } 13 } 14 import { createReadStream } from 'fs' import { once } from 'events' const sourceStream = createReadStream('bigdata.csv') const destStream = new SlowTransform() for await (const chunk of sourceStream) { const canContinue = destStream.write(chunk) if (!canContinue) { // backpressure, now we stop and we need to wait for drain await once(destStream, 'drain') // ok now it's safe to resume writing } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @loige 48
  105. import { pipeline } from 'stream/promises' import { createReadStream, createWriteStream

    } from 'fs' import { createBrotliCompress } from 'zlib' const sourceStream = createReadStream('bigdata.csv') const compress = createBrotliCompress() const destStream = createWriteStream('bigdata.csv.br') await pipeline( sourceStream, compress, destStream ) 1 2 3 4 5 6 7 8 9 10 11 12 13 @loige 50
  106. import { pipeline } from 'stream/promises' import { createReadStream, createWriteStream

    } from 'fs' import { createBrotliCompress } from 'zlib' const sourceStream = createReadStream('bigdata.csv') const compress = createBrotliCompress() const destStream = createWriteStream('bigdata.csv.br') await pipeline( sourceStream, compress, destStream ) 1 2 3 4 5 6 7 8 9 10 11 12 13 import { pipeline } from 'stream/promises' 1 import { createReadStream, createWriteStream } from 'fs' 2 import { createBrotliCompress } from 'zlib' 3 4 const sourceStream = createReadStream('bigdata.csv') 5 const compress = createBrotliCompress() 6 const destStream = createWriteStream('bigdata.csv.br') 7 8 await pipeline( 9 sourceStream, 10 compress, 11 destStream 12 ) 13 @loige 50
  107. import { pipeline } from 'stream/promises' import { createReadStream, createWriteStream

    } from 'fs' import { createBrotliCompress } from 'zlib' const sourceStream = createReadStream('bigdata.csv') const compress = createBrotliCompress() const destStream = createWriteStream('bigdata.csv.br') await pipeline( sourceStream, compress, destStream ) 1 2 3 4 5 6 7 8 9 10 11 12 13 import { pipeline } from 'stream/promises' 1 import { createReadStream, createWriteStream } from 'fs' 2 import { createBrotliCompress } from 'zlib' 3 4 const sourceStream = createReadStream('bigdata.csv') 5 const compress = createBrotliCompress() 6 const destStream = createWriteStream('bigdata.csv.br') 7 8 await pipeline( 9 sourceStream, 10 compress, 11 destStream 12 ) 13 import { createReadStream, createWriteStream } from 'fs' import { createBrotliCompress } from 'zlib' import { pipeline } from 'stream/promises' 1 2 3 4 const sourceStream = createReadStream('bigdata.csv') 5 const compress = createBrotliCompress() 6 const destStream = createWriteStream('bigdata.csv.br') 7 8 await pipeline( 9 sourceStream, 10 compress, 11 destStream 12 ) 13 @loige 50
  108. import { pipeline } from 'stream/promises' import { createReadStream, createWriteStream

    } from 'fs' import { createBrotliCompress } from 'zlib' const sourceStream = createReadStream('bigdata.csv') const compress = createBrotliCompress() const destStream = createWriteStream('bigdata.csv.br') await pipeline( sourceStream, compress, destStream ) 1 2 3 4 5 6 7 8 9 10 11 12 13 import { pipeline } from 'stream/promises' 1 import { createReadStream, createWriteStream } from 'fs' 2 import { createBrotliCompress } from 'zlib' 3 4 const sourceStream = createReadStream('bigdata.csv') 5 const compress = createBrotliCompress() 6 const destStream = createWriteStream('bigdata.csv.br') 7 8 await pipeline( 9 sourceStream, 10 compress, 11 destStream 12 ) 13 import { createReadStream, createWriteStream } from 'fs' import { createBrotliCompress } from 'zlib' import { pipeline } from 'stream/promises' 1 2 3 4 const sourceStream = createReadStream('bigdata.csv') 5 const compress = createBrotliCompress() 6 const destStream = createWriteStream('bigdata.csv.br') 7 8 await pipeline( 9 sourceStream, 10 compress, 11 destStream 12 ) 13 const sourceStream = createReadStream('bigdata.csv') const compress = createBrotliCompress() const destStream = createWriteStream('bigdata.csv.br') import { pipeline } from 'stream/promises' 1 import { createReadStream, createWriteStream } from 'fs' 2 import { createBrotliCompress } from 'zlib' 3 4 5 6 7 8 await pipeline( 9 sourceStream, 10 compress, 11 destStream 12 ) 13 @loige 50
  109. import { pipeline } from 'stream/promises' import { createReadStream, createWriteStream

    } from 'fs' import { createBrotliCompress } from 'zlib' const sourceStream = createReadStream('bigdata.csv') const compress = createBrotliCompress() const destStream = createWriteStream('bigdata.csv.br') await pipeline( sourceStream, compress, destStream ) 1 2 3 4 5 6 7 8 9 10 11 12 13 import { pipeline } from 'stream/promises' 1 import { createReadStream, createWriteStream } from 'fs' 2 import { createBrotliCompress } from 'zlib' 3 4 const sourceStream = createReadStream('bigdata.csv') 5 const compress = createBrotliCompress() 6 const destStream = createWriteStream('bigdata.csv.br') 7 8 await pipeline( 9 sourceStream, 10 compress, 11 destStream 12 ) 13 import { createReadStream, createWriteStream } from 'fs' import { createBrotliCompress } from 'zlib' import { pipeline } from 'stream/promises' 1 2 3 4 const sourceStream = createReadStream('bigdata.csv') 5 const compress = createBrotliCompress() 6 const destStream = createWriteStream('bigdata.csv.br') 7 8 await pipeline( 9 sourceStream, 10 compress, 11 destStream 12 ) 13 const sourceStream = createReadStream('bigdata.csv') const compress = createBrotliCompress() const destStream = createWriteStream('bigdata.csv.br') import { pipeline } from 'stream/promises' 1 import { createReadStream, createWriteStream } from 'fs' 2 import { createBrotliCompress } from 'zlib' 3 4 5 6 7 8 await pipeline( 9 sourceStream, 10 compress, 11 destStream 12 ) 13 await pipeline( sourceStream, compress, destStream ) import { pipeline } from 'stream/promises' 1 import { createReadStream, createWriteStream } from 'fs' 2 import { createBrotliCompress } from 'zlib' 3 4 const sourceStream = createReadStream('bigdata.csv') 5 const compress = createBrotliCompress() 6 const destStream = createWriteStream('bigdata.csv.br') 7 8 9 10 11 12 13 @loige 50
  110. import { pipeline } from 'stream/promises' import { createReadStream, createWriteStream

    } from 'fs' import { createBrotliCompress } from 'zlib' const sourceStream = createReadStream('bigdata.csv') const compress = createBrotliCompress() const destStream = createWriteStream('bigdata.csv.br') await pipeline( sourceStream, compress, destStream ) 1 2 3 4 5 6 7 8 9 10 11 12 13 import { pipeline } from 'stream/promises' 1 import { createReadStream, createWriteStream } from 'fs' 2 import { createBrotliCompress } from 'zlib' 3 4 const sourceStream = createReadStream('bigdata.csv') 5 const compress = createBrotliCompress() 6 const destStream = createWriteStream('bigdata.csv.br') 7 8 await pipeline( 9 sourceStream, 10 compress, 11 destStream 12 ) 13 import { createReadStream, createWriteStream } from 'fs' import { createBrotliCompress } from 'zlib' import { pipeline } from 'stream/promises' 1 2 3 4 const sourceStream = createReadStream('bigdata.csv') 5 const compress = createBrotliCompress() 6 const destStream = createWriteStream('bigdata.csv.br') 7 8 await pipeline( 9 sourceStream, 10 compress, 11 destStream 12 ) 13 const sourceStream = createReadStream('bigdata.csv') const compress = createBrotliCompress() const destStream = createWriteStream('bigdata.csv.br') import { pipeline } from 'stream/promises' 1 import { createReadStream, createWriteStream } from 'fs' 2 import { createBrotliCompress } from 'zlib' 3 4 5 6 7 8 await pipeline( 9 sourceStream, 10 compress, 11 destStream 12 ) 13 await pipeline( sourceStream, compress, destStream ) import { pipeline } from 'stream/promises' 1 import { createReadStream, createWriteStream } from 'fs' 2 import { createBrotliCompress } from 'zlib' 3 4 const sourceStream = createReadStream('bigdata.csv') 5 const compress = createBrotliCompress() 6 const destStream = createWriteStream('bigdata.csv.br') 7 8 9 10 11 12 13 import { pipeline } from 'stream/promises' import { createReadStream, createWriteStream } from 'fs' import { createBrotliCompress } from 'zlib' const sourceStream = createReadStream('bigdata.csv') const compress = createBrotliCompress() const destStream = createWriteStream('bigdata.csv.br') await pipeline( sourceStream, compress, destStream ) 1 2 3 4 5 6 7 8 9 10 11 12 13 @loige 50
  111. In Node.js we can convert any Event Emitter to an

    Async Iterator! 😱 @loige 51
  112. import { on } from 'events' import glob from 'glob'

    // from npm const matcher = glob('**/*.js') for await (const [filePath] of on(matcher, 'match')) { console.log(filePath) } 1 2 3 4 5 6 7 8 @loige 52
  113. import { on } from 'events' import glob from 'glob'

    // from npm const matcher = glob('**/*.js') for await (const [filePath] of on(matcher, 'match')) { console.log(filePath) } 1 2 3 4 5 6 7 8 import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 @loige 52
  114. import { on } from 'events' import glob from 'glob'

    // from npm const matcher = glob('**/*.js') for await (const [filePath] of on(matcher, 'match')) { console.log(filePath) } 1 2 3 4 5 6 7 8 import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 import glob from 'glob' // from npm import { on } from 'events' 1 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 @loige 52
  115. import { on } from 'events' import glob from 'glob'

    // from npm const matcher = glob('**/*.js') for await (const [filePath] of on(matcher, 'match')) { console.log(filePath) } 1 2 3 4 5 6 7 8 import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 import glob from 'glob' // from npm import { on } from 'events' 1 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 const matcher = glob('**/*.js') import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 @loige 52
  116. import { on } from 'events' import glob from 'glob'

    // from npm const matcher = glob('**/*.js') for await (const [filePath] of on(matcher, 'match')) { console.log(filePath) } 1 2 3 4 5 6 7 8 import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 import glob from 'glob' // from npm import { on } from 'events' 1 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 const matcher = glob('**/*.js') import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 for await (const [filePath] of on(matcher, 'match')) { } import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 6 console.log(filePath) 7 8 @loige 52
  117. import { on } from 'events' import glob from 'glob'

    // from npm const matcher = glob('**/*.js') for await (const [filePath] of on(matcher, 'match')) { console.log(filePath) } 1 2 3 4 5 6 7 8 import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 import glob from 'glob' // from npm import { on } from 'events' 1 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 const matcher = glob('**/*.js') import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 for await (const [filePath] of on(matcher, 'match')) { } import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 6 console.log(filePath) 7 8 console.log(filePath) import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 7 } 8 @loige 52
  118. import { on } from 'events' import glob from 'glob'

    // from npm const matcher = glob('**/*.js') for await (const [filePath] of on(matcher, 'match')) { console.log(filePath) } 1 2 3 4 5 6 7 8 import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 import glob from 'glob' // from npm import { on } from 'events' 1 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 const matcher = glob('**/*.js') import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 for await (const [filePath] of on(matcher, 'match')) { } import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 6 console.log(filePath) 7 8 console.log(filePath) import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 7 } 8 import { on } from 'events' import glob from 'glob' // from npm const matcher = glob('**/*.js') for await (const [filePath] of on(matcher, 'match')) { console.log(filePath) } 1 2 3 4 5 6 7 8 @loige 52
  119. import { on } from 'events' import glob from 'glob'

    // from npm const matcher = glob('**/*.js') for await (const [filePath] of on(matcher, 'match')) { console.log(filePath) } // ⚠ DANGER, DANGER (high voltage ⚡ ): We'll never get here! console.log('ALL DONE! :)') 1 2 3 4 5 6 7 8 9 10 11 @loige 53
  120. import { on } from 'events' import glob from 'glob'

    // from npm const matcher = glob('**/*.js') for await (const [filePath] of on(matcher, 'match')) { console.log(filePath) } // ⚠ DANGER, DANGER (high voltage ⚡ ): We'll never get here! console.log('ALL DONE! :)') 1 2 3 4 5 6 7 8 9 10 11 // ⚠ DANGER, DANGER (high voltage ⚡ ): We'll never get here! console.log('ALL DONE! :)') import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 9 10 11 @loige 53
  121. import { on } from 'events' import glob from 'glob'

    // from npm const matcher = glob('**/*.js') for await (const [filePath] of on(matcher, 'match')) { console.log(filePath) } // ⚠ DANGER, DANGER (high voltage ⚡ ): We'll never get here! console.log('ALL DONE! :)') 1 2 3 4 5 6 7 8 9 10 11 // ⚠ DANGER, DANGER (high voltage ⚡ ): We'll never get here! console.log('ALL DONE! :)') import { on } from 'events' 1 import glob from 'glob' // from npm 2 3 const matcher = glob('**/*.js') 4 5 for await (const [filePath] of on(matcher, 'match')) { 6 console.log(filePath) 7 } 8 9 10 11 import { on } from 'events' import glob from 'glob' // from npm const matcher = glob('**/*.js') for await (const [filePath] of on(matcher, 'match')) { console.log(filePath) } // ⚠ DANGER, DANGER (high voltage ⚡ ): We'll never get here! console.log('ALL DONE! :)') 1 2 3 4 5 6 7 8 9 10 11 @loige 53
  122. import { on } from 'events' import glob from 'glob'

    const matcher = glob('**/*.js') const ac = new global.AbortController() matcher.once('end', () => ac.abort()) try { for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { console.log(`./${filePath}`) } } catch (err) { if (!ac.signal.aborted) { console.error(err) process.exit(1) } // we ignore the AbortError } console.log('NOW WE GETTING HERE! :)') // YAY! 😻 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @loige 54
  123. import { on } from 'events' import glob from 'glob'

    const matcher = glob('**/*.js') const ac = new global.AbortController() matcher.once('end', () => ac.abort()) try { for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { console.log(`./${filePath}`) } } catch (err) { if (!ac.signal.aborted) { console.error(err) process.exit(1) } // we ignore the AbortError } console.log('NOW WE GETTING HERE! :)') // YAY! 😻 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const ac = new global.AbortController() import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 @loige 54
  124. import { on } from 'events' import glob from 'glob'

    const matcher = glob('**/*.js') const ac = new global.AbortController() matcher.once('end', () => ac.abort()) try { for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { console.log(`./${filePath}`) } } catch (err) { if (!ac.signal.aborted) { console.error(err) process.exit(1) } // we ignore the AbortError } console.log('NOW WE GETTING HERE! :)') // YAY! 😻 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const ac = new global.AbortController() import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 matcher.once('end', () => ac.abort()) import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 @loige 54
  125. import { on } from 'events' import glob from 'glob'

    const matcher = glob('**/*.js') const ac = new global.AbortController() matcher.once('end', () => ac.abort()) try { for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { console.log(`./${filePath}`) } } catch (err) { if (!ac.signal.aborted) { console.error(err) process.exit(1) } // we ignore the AbortError } console.log('NOW WE GETTING HERE! :)') // YAY! 😻 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const ac = new global.AbortController() import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 matcher.once('end', () => ac.abort()) import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 try { } catch (err) { // we ignore the AbortError import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 @loige 54
  126. import { on } from 'events' import glob from 'glob'

    const matcher = glob('**/*.js') const ac = new global.AbortController() matcher.once('end', () => ac.abort()) try { for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { console.log(`./${filePath}`) } } catch (err) { if (!ac.signal.aborted) { console.error(err) process.exit(1) } // we ignore the AbortError } console.log('NOW WE GETTING HERE! :)') // YAY! 😻 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const ac = new global.AbortController() import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 matcher.once('end', () => ac.abort()) import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 try { } catch (err) { // we ignore the AbortError import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 if (!ac.signal.aborted) { console.error(err) process.exit(1) } import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 14 15 16 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 @loige 54
  127. import { on } from 'events' import glob from 'glob'

    const matcher = glob('**/*.js') const ac = new global.AbortController() matcher.once('end', () => ac.abort()) try { for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { console.log(`./${filePath}`) } } catch (err) { if (!ac.signal.aborted) { console.error(err) process.exit(1) } // we ignore the AbortError } console.log('NOW WE GETTING HERE! :)') // YAY! 😻 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const ac = new global.AbortController() import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 matcher.once('end', () => ac.abort()) import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 try { } catch (err) { // we ignore the AbortError import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 if (!ac.signal.aborted) { console.error(err) process.exit(1) } import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 14 15 16 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 // we ignore the AbortError import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 @loige 54
  128. import { on } from 'events' import glob from 'glob'

    const matcher = glob('**/*.js') const ac = new global.AbortController() matcher.once('end', () => ac.abort()) try { for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { console.log(`./${filePath}`) } } catch (err) { if (!ac.signal.aborted) { console.error(err) process.exit(1) } // we ignore the AbortError } console.log('NOW WE GETTING HERE! :)') // YAY! 😻 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const ac = new global.AbortController() import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 matcher.once('end', () => ac.abort()) import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 try { } catch (err) { // we ignore the AbortError import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 if (!ac.signal.aborted) { console.error(err) process.exit(1) } import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 14 15 16 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 // we ignore the AbortError import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 21 @loige 54
  129. import { on } from 'events' import glob from 'glob'

    const matcher = glob('**/*.js') const ac = new global.AbortController() matcher.once('end', () => ac.abort()) try { for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { console.log(`./${filePath}`) } } catch (err) { if (!ac.signal.aborted) { console.error(err) process.exit(1) } // we ignore the AbortError } console.log('NOW WE GETTING HERE! :)') // YAY! 😻 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const ac = new global.AbortController() import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 matcher.once('end', () => ac.abort()) import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 try { } catch (err) { // we ignore the AbortError import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 if (!ac.signal.aborted) { console.error(err) process.exit(1) } import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 14 15 16 17 // we ignore the AbortError 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 // we ignore the AbortError import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 18 } 19 20 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 21 console.log('NOW WE GETTING HERE! :)') // YAY! 😻 import { on } from 'events' 1 import glob from 'glob' 2 3 const matcher = glob('**/*.js') 4 const ac = new global.AbortController() 5 6 matcher.once('end', () => ac.abort()) 7 8 try { 9 for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { 10 console.log(`./${filePath}`) 11 } 12 } catch (err) { 13 if (!ac.signal.aborted) { 14 console.error(err) 15 process.exit(1) 16 } 17 // we ignore the AbortError 18 } 19 20 21 import { on } from 'events' import glob from 'glob' const matcher = glob('**/*.js') const ac = new global.AbortController() matcher.once('end', () => ac.abort()) try { for await (const [filePath] of on(matcher, 'match', { signal: ac.signal })) { console.log(`./${filePath}`) } } catch (err) { if (!ac.signal.aborted) { console.error(err) process.exit(1) } // we ignore the AbortError } console.log('NOW WE GETTING HERE! :)') // YAY! 😻 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @loige 54
  130. NOTE: If you know ahead of time how many events

    you need to process you can also use a break in the for...await loop. @loige 55
  131. LAST TIP: Can we use async iterators to handle web

    requests a-la-Deno? 🦕 @loige 56
  132. import { createServer } from 'http' import { on }

    from 'events' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { res.end('hello dear friend') } 1 2 3 4 5 6 7 8 9 @loige 57
  133. import { createServer } from 'http' import { on }

    from 'events' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { res.end('hello dear friend') } 1 2 3 4 5 6 7 8 9 import { createServer } from 'http' import { on } from 'events' 1 2 3 const server = createServer() 4 server.listen(8000) 5 6 for await (const [req, res] of on(server, 'request')) { 7 res.end('hello dear friend') 8 } 9 @loige 57
  134. import { createServer } from 'http' import { on }

    from 'events' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { res.end('hello dear friend') } 1 2 3 4 5 6 7 8 9 import { createServer } from 'http' import { on } from 'events' 1 2 3 const server = createServer() 4 server.listen(8000) 5 6 for await (const [req, res] of on(server, 'request')) { 7 res.end('hello dear friend') 8 } 9 const server = createServer() server.listen(8000) import { createServer } from 'http' 1 import { on } from 'events' 2 3 4 5 6 for await (const [req, res] of on(server, 'request')) { 7 res.end('hello dear friend') 8 } 9 @loige 57
  135. import { createServer } from 'http' import { on }

    from 'events' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { res.end('hello dear friend') } 1 2 3 4 5 6 7 8 9 import { createServer } from 'http' import { on } from 'events' 1 2 3 const server = createServer() 4 server.listen(8000) 5 6 for await (const [req, res] of on(server, 'request')) { 7 res.end('hello dear friend') 8 } 9 const server = createServer() server.listen(8000) import { createServer } from 'http' 1 import { on } from 'events' 2 3 4 5 6 for await (const [req, res] of on(server, 'request')) { 7 res.end('hello dear friend') 8 } 9 for await (const [req, res] of on(server, 'request')) { } import { createServer } from 'http' 1 import { on } from 'events' 2 3 const server = createServer() 4 server.listen(8000) 5 6 7 res.end('hello dear friend') 8 9 @loige 57
  136. import { createServer } from 'http' import { on }

    from 'events' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { res.end('hello dear friend') } 1 2 3 4 5 6 7 8 9 import { createServer } from 'http' import { on } from 'events' 1 2 3 const server = createServer() 4 server.listen(8000) 5 6 for await (const [req, res] of on(server, 'request')) { 7 res.end('hello dear friend') 8 } 9 const server = createServer() server.listen(8000) import { createServer } from 'http' 1 import { on } from 'events' 2 3 4 5 6 for await (const [req, res] of on(server, 'request')) { 7 res.end('hello dear friend') 8 } 9 for await (const [req, res] of on(server, 'request')) { } import { createServer } from 'http' 1 import { on } from 'events' 2 3 const server = createServer() 4 server.listen(8000) 5 6 7 res.end('hello dear friend') 8 9 res.end('hello dear friend') import { createServer } from 'http' 1 import { on } from 'events' 2 3 const server = createServer() 4 server.listen(8000) 5 6 for await (const [req, res] of on(server, 'request')) { 7 8 } 9 @loige 57
  137. import { createServer } from 'http' import { on }

    from 'events' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { res.end('hello dear friend') } 1 2 3 4 5 6 7 8 9 import { createServer } from 'http' import { on } from 'events' 1 2 3 const server = createServer() 4 server.listen(8000) 5 6 for await (const [req, res] of on(server, 'request')) { 7 res.end('hello dear friend') 8 } 9 const server = createServer() server.listen(8000) import { createServer } from 'http' 1 import { on } from 'events' 2 3 4 5 6 for await (const [req, res] of on(server, 'request')) { 7 res.end('hello dear friend') 8 } 9 for await (const [req, res] of on(server, 'request')) { } import { createServer } from 'http' 1 import { on } from 'events' 2 3 const server = createServer() 4 server.listen(8000) 5 6 7 res.end('hello dear friend') 8 9 res.end('hello dear friend') import { createServer } from 'http' 1 import { on } from 'events' 2 3 const server = createServer() 4 server.listen(8000) 5 6 for await (const [req, res] of on(server, 'request')) { 7 8 } 9 import { createServer } from 'http' import { on } from 'events' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { res.end('hello dear friend') } 1 2 3 4 5 6 7 8 9 @loige 57
  138. EASY PEASY LEMON SQUEEZY! 🍋 But... wait, aren't we processing

    all requests in series, now? 😱 @loige 58
  139. import { createServer } from 'http' import { on }

    from 'events' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { // ... AS LONG AS WE DON'T USE await HERE, WE ARE FINE! } 1 2 3 4 5 6 7 8 9 @loige 59
  140. import { createServer } from 'http' import { on }

    from 'events' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { // ... AS LONG AS WE DON'T USE await HERE, WE ARE FINE! } 1 2 3 4 5 6 7 8 9 // ... AS LONG AS WE DON'T USE await HERE, WE ARE FINE! import { createServer } from 'http' 1 import { on } from 'events' 2 3 const server = createServer() 4 server.listen(8000) 5 6 for await (const [req, res] of on(server, 'request')) { 7 8 } 9 @loige 59
  141. import { createServer } from 'http' import { on }

    from 'events' import { setTimeout } from 'timers/promises' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { await setTimeout(1000) res.end('hello dear friend') } 1 2 3 4 5 6 7 8 9 10 11 @loige 60
  142. import { createServer } from 'http' import { on }

    from 'events' import { setTimeout } from 'timers/promises' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { await setTimeout(1000) res.end('hello dear friend') } 1 2 3 4 5 6 7 8 9 10 11 import { setTimeout } from 'timers/promises' import { createServer } from 'http' 1 import { on } from 'events' 2 3 4 const server = createServer() 5 server.listen(8000) 6 7 for await (const [req, res] of on(server, 'request')) { 8 await setTimeout(1000) 9 res.end('hello dear friend') 10 } 11 @loige 60
  143. import { createServer } from 'http' import { on }

    from 'events' import { setTimeout } from 'timers/promises' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { await setTimeout(1000) res.end('hello dear friend') } 1 2 3 4 5 6 7 8 9 10 11 import { setTimeout } from 'timers/promises' import { createServer } from 'http' 1 import { on } from 'events' 2 3 4 const server = createServer() 5 server.listen(8000) 6 7 for await (const [req, res] of on(server, 'request')) { 8 await setTimeout(1000) 9 res.end('hello dear friend') 10 } 11 await setTimeout(1000) import { createServer } from 'http' 1 import { on } from 'events' 2 import { setTimeout } from 'timers/promises' 3 4 const server = createServer() 5 server.listen(8000) 6 7 for await (const [req, res] of on(server, 'request')) { 8 9 res.end('hello dear friend') 10 } 11 @loige 60
  144. import { createServer } from 'http' import { on }

    from 'events' import { setTimeout } from 'timers/promises' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { await setTimeout(1000) res.end('hello dear friend') } 1 2 3 4 5 6 7 8 9 10 11 import { setTimeout } from 'timers/promises' import { createServer } from 'http' 1 import { on } from 'events' 2 3 4 const server = createServer() 5 server.listen(8000) 6 7 for await (const [req, res] of on(server, 'request')) { 8 await setTimeout(1000) 9 res.end('hello dear friend') 10 } 11 await setTimeout(1000) import { createServer } from 'http' 1 import { on } from 'events' 2 import { setTimeout } from 'timers/promises' 3 4 const server = createServer() 5 server.listen(8000) 6 7 for await (const [req, res] of on(server, 'request')) { 8 9 res.end('hello dear friend') 10 } 11 import { createServer } from 'http' import { on } from 'events' import { setTimeout } from 'timers/promises' const server = createServer() server.listen(8000) for await (const [req, res] of on(server, 'request')) { await setTimeout(1000) res.end('hello dear friend') } 1 2 3 4 5 6 7 8 9 10 11 @loige 60
  145. import { createServer } from 'http' import { setTimeout }

    from 'timers/promises' const server = createServer(async function (req, res) { await setTimeout(1000) res.end('hello dear friend') }) server.listen(8000) 1 2 3 4 5 6 7 8 9 @loige 63
  146. import { createServer } from 'http' import { setTimeout }

    from 'timers/promises' const server = createServer(async function (req, res) { await setTimeout(1000) res.end('hello dear friend') }) server.listen(8000) 1 2 3 4 5 6 7 8 9 const server = createServer(async function (req, res) { }) import { createServer } from 'http' 1 import { setTimeout } from 'timers/promises' 2 3 4 await setTimeout(1000) 5 res.end('hello dear friend') 6 7 8 server.listen(8000) 9 @loige 63
  147. import { createServer } from 'http' import { setTimeout }

    from 'timers/promises' const server = createServer(async function (req, res) { await setTimeout(1000) res.end('hello dear friend') }) server.listen(8000) 1 2 3 4 5 6 7 8 9 const server = createServer(async function (req, res) { }) import { createServer } from 'http' 1 import { setTimeout } from 'timers/promises' 2 3 4 await setTimeout(1000) 5 res.end('hello dear friend') 6 7 8 server.listen(8000) 9 import { createServer } from 'http' import { setTimeout } from 'timers/promises' const server = createServer(async function (req, res) { await setTimeout(1000) res.end('hello dear friend') }) server.listen(8000) 1 2 3 4 5 6 7 8 9 @loige 63
  148. Conclusion 🤓 @loige Iterable protocols are a way to standardize

    iteration in JavaScript and Node.js Async iterators are ergonomic tools for sequential asynchronous iteration But don't use them for everything! Consuming data from paginated APIs or reading messages from a queue are good examples! Handling web requests or events from an emitter might not be the best use cases! 65
  149. Want to learn more? @loige - possibly a great book

    😇 - In-depth article on all things iterators - Finding a lost song with Node.js & async iterators nodejsdesignpatterns.com nodejsdesignpatterns.com/blog/javascript-async- iterators/ loige.link/async-it 66
  150. If you enjoyed this talk, you might also enjoy 😛

    nodejsdp.link @loige Let's connect: (blog) (twitter) (twitch) (github) loige.co @loige loige lmammino loige.link/iter 67