A, B, C. 1, 2, 3. Iterables you and me.

A, B, C. 1, 2, 3. Iterables you and me.

The Iterable protocol was introduced in 2015, but it wasn't really caught on, and people have doubts regarding how it works, how can we leverage it to write better and more expressive code. This talk tries to break this fantastic ECMAScript feature down in a one-two step, showing little by little the use cases, properties, and the *new async Iterator protocol*, quickly and smoothly, like trying to learn how to dance this fun Jackson's 5 Soul music ;) If you are a beginner in JS, you will learn how to build custom iterable objects in a bunch of different ways, and if you already got it, I will challenge you to go an extra mile and experiment neat tricks like composing iterables or creating a PoC of a state/side effect management based on Iterables.

E807477655ad7125c8731065178030e2?s=128

Willian Martins

May 30, 2019
Tweet

Transcript

  1. @wmsbill Zdravo!

  2. @wmsbill

  3. SHIFT DEV 2019

  4. @wmsbill Why Iterate?

  5. @wmsbill How to iterate?

  6. let collection = [1,2,3,4,5,6]; let sum = 0; for (let

    i = 0; i < collection.length; i++) { sum = sum + collection[i]; } console.log(sum); // 21 @wmsbill
  7. let collection = [1,2,3,4,5,6]; let sum = 0, i =

    0; while (i < collection.length) { sum = sum + collection[i]; i++; } console.log(sum); // 21 @wmsbill
  8. let collection = [1,2,3,4,5,6]; let sum = 0, i =

    0; do { sum = sum + collection[i]; i++; } while (i < collection.length); console.log(sum); // 21 @wmsbill
  9. let collection = [1,2,3,4,5,6]; let sum = 0; for (let

    i in collection) { sum = sum + collection[i]; } console.log(sum); // 21 @wmsbill
  10. Array.prototype.foo = “bar”; let collection = [1,2,3,4,5,6]; let sum =

    0; for (let i in collection) { sum = sum + i; } console.log(sum); // 21bar @wmsbill
  11. Array.prototype.foo = “bar”; let collection = [1,2,3,4,5,6]; let sum =

    0; for (let i in collection) { if(collection.hasOwnProperty(i)) { sum = sum + collection[i]; } } console.log(sum); // 21 @wmsbill
  12. @wmsbill Iterating on ES5

  13. let collection = [1,2,3,4,5,6]; let sum = 0; collection.forEach((num) =>

    sum = sum + num); console.log(sum); // 21 @wmsbill
  14. let collection = [1,2,3,4,5,6]; collection.reduce((sum, num) => sum + num,

    0); console.log(sum); // 21 @wmsbill
  15. @wmsbill Expressiveness

  16. @wmsbill Expressiveness With a cost

  17. { "23": {id: "23", name: "soccer ball", "value": 30}, "42":

    {id: "42", name: "sneakers", "value": 50}, "12": {id: "12", name: "belt", "value": 10}, "30": {id: "30", name: "baseball bat", "value": 90}, "74": {id: "74", name: "sunglasses", "value": 25}, "92": {id: "92", name: "cap", "value": 40} } @wmsbill
  18. @wmsbill Iterables!

  19. @wmsbill GoF Design patterns?

  20. In object-oriented programming, the iterator pattern is a design pattern

    in which an iterator is used to traverse a container and access the container's elements. The iterator pattern decouples algorithms from containers; GoF Design Patterns Book
  21. In object-oriented programming, the iterator pattern is a design pattern

    in which an iterator is used to traverse a container and access the container's elements. The iterator pattern decouples algorithms from containers; Wikipedia
  22. @wmsbill Iterator interface?

  23. @wmsbill Iterator protocol!

  24. interface Iterator { next() : IteratorResult; } interface IteratorResult {

    value: any; done: boolean; } @wmsbill
  25. @wmsbill Iterable protocol!

  26. interface Iterable { [Symbol.iterator]() : Iterator; } @wmsbill

  27. Array String Map Set TypedArray

  28. Map/WeakMap Set/WeakSet Promise.all Promise.race Array.from

  29. for-of yield* … spread operator Destructuring assignment

  30. @wmsbill How to create an Iterable?

  31. function range (min, max) { return { next () {

    return { value: min++, done: min > max, } } } } @wmsbill Iterator Result Iterator
  32. const myRange = range(10, 20); let next = myRange.next(); while

    (!next.done) { console.log(next.value); next = myRange.next(); } @wmsbill
  33. function range (min, max) { return { [Symbol.iterator] () {

    return { next () { return { value: min++, done: min > max, } } } } } } @wmsbill Iterator Result Iterator Iterable
  34. for (const num of range(10, 20)) { console.info(num); }

  35. [...range(0,5)]; // [0, 1, 2, 3, 4] @wmsbill

  36. const [head, ...tail] = range(0, 5); console.info(head); // 0 console.info(tail);

    // [1, 2, 3, 4] @wmsbill
  37. function range (min, max) { return { [Symbol.iterator] () {

    return { next () { return { value: min++, done: min > max, } } } } } } @wmsbill
  38. @wmsbill Generators

  39. function range (min, max) { return { *[Symbol.iterator] () {

    while (min < max) { yield min++; } } } } @wmsbill
  40. function* range (min, max) { while (min < max) {

    yield min++; } } @wmsbill
  41. @wmsbill What I can do with it? A.K.A iterables on

    steroids
  42. [...range(10, 100)] .filter(isMultipleOfFour) .map(double) .slice(0, 5) @wmsbill

  43. const map = (iterable, func) => function* () { for

    (item of iterable) { yield func(item); } }(); @wmsbill
  44. const filter = (iterable, func) => function* () { for

    (item of iterable) { if (func(item)) { yield item; } } }(); @wmsbill
  45. const take = (iterable, num) => function* () { let

    count = 0; for (item of iterable) { if (count++ === num) { return; } yield item; } }(); @wmsbill
  46. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), ); @wmsbill
  47. @wmsbill Nothing Iterables are lazy!

  48. const sink = iterable => [...iterable]; @wmsbill

  49. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); // [ 24, 32, 40, 48, 56 ] @wmsbill
  50. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); [...range(10, 100)] .filter(isMultipleOfFour) .map(double) .slice(0, 5)
  51. [...range(10, 100)] .filter(isMultipleOfFour) .map(double) .slice(0, 5) 10 11 12 13

    14 15 … 99
  52. [...range(10, 100)] .filter(isMultipleOfFour) .map(double) .slice(0, 5) 10 11 12 13

    14 15 … 99
  53. [...range(10, 100)] .filter(isMultipleOfFour) .map(double) .slice(0, 5) 12 16 20 24

    28 32 … 96
  54. [...range(10, 100)] .filter(isMultipleOfFour) .map(double) .slice(0, 5) 24 32 40 48

    56 64 … 192
  55. [...range(10, 100)] .filter(isMultipleOfFour) .map(double) .slice(0, 5) 24 32 40 48

    56
  56. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); next()
  57. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); next()
  58. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); next()
  59. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); 12
  60. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); 24
  61. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); 24
  62. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); 24
  63. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); 24 next()
  64. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); next() 24
  65. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); next() 24
  66. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); 16 24
  67. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); 32 24
  68. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); 32 24
  69. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); 32 24
  70. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); 32 24 40 48 56 return
  71. pipe( filter(range(10, 100), isMultipleOfFour), result => map(result, double), result =>

    take(result, 5), sink ); 32 24 40 48 56
  72. @wmsbill That’s all? Not yet

  73. @wmsbill Async Iterables

  74. interface AsyncIterable { [Symbol.asyncIterator]() : AsyncIterator; } interface AsyncIterator {

    next() : Promise<IteratorResult>; } interface IteratorResult { value: any; done: boolean; } @wmsbill
  75. @wmsbill Can I use async generator for it? Yes, you

    can!
  76. async function* range (min, max) { while (min < max)

    { yield min++; } } @wmsbill
  77. @wmsbill How to sink an async Iterator?

  78. for await (const number of range(10, 100)) { console.log(number); }

    @wmsbill
  79. @wmsbill Async Iterable is a Pull Stream

  80. @wmsbill https://github.com/whatwg/streams

  81. https://github.com/wmsbill/eita https://github.com/whatwg/streams https://github.com/rauschma/async-iter-demo http://2ality.com/2016/10/asynchronous-iteration.html https://github.com/ReactiveX/IxJS https://medium.com/@jayphelps/backpressure-explained-the-flow- of-data-through-software-2350b3e77ce7 https://jakearchibald.com/2017/async-iterators-and-generators/ https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/ Iterators_and_Generators

    http://exploringjs.com/es6/ch_iteration.html https://developer.mozilla.org/en-US/docs/Web/JavaScript/ Reference/Global_Objects/Symbol/asyncIterator
  82. Contact https://medium.com/@wmsbill https://twitter.com/wmsbill wmsbill@gmail.com

  83. None