$30 off During Our Annual Pro Sale. View Details »

DevNexus 2017: The Rise of Async JavaScript

DevNexus 2017: The Rise of Async JavaScript

Jeremy Fairbank

February 24, 2017
Tweet

More Decks by Jeremy Fairbank

Other Decks in Programming

Transcript

  1. the RISEof async
    JavaScript
    Jeremy Fairbank
    blog.jeremyfairbank.com
    @elpapapollo | gh/jfairbank

    View Slide

  2. sigient.com

    View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. “Call me maybe?”

    View Slide

  8. Async API
    Callback

    View Slide

  9. Callback
    fetchUserById(1, function(err, user) {
    if (err) {
    console.error('Could not retrieve user');
    } else {
    console.log(user);
    }
    });

    View Slide

  10. });
    });
    });
    });
    });
    });
    Callback
    Callback
    Callback
    Callback
    Callback
    Callback

    View Slide

  11. function fetchCustomerNameForOrder(orderId, done, fail) {
    fetchOrder(orderId, function(err, order) {
    if (err) {
    logError(err);
    fail(err);
    } else {
    fetchCustomer(
    order.customerId,
    function(err, customer) {
    if (err) {
    logError(err);
    fail(err);
    } else {
    done(customer.name);
    }
    }
    );
    }
    });
    }

    View Slide

  12. function fetchCustomerNameForOrder(orderId, done, fail) {
    fetchOrder(orderId, function(err, order) {
    if (err) {
    logError(err);
    fail(err);
    } else {
    fetchCustomer(
    order.customerId,
    function(err, customer) {
    if (err) {
    logError(err);
    fail(err);
    } else {
    done(customer.name);
    }
    }
    );
    }
    });
    }

    View Slide

  13. function fetchCustomerNameForOrder(orderId, done, fail) {
    fetchOrder(orderId, function(err, order) {
    if (err) {
    logError(err);
    fail(err);
    } else {
    fetchCustomer(
    order.customerId,
    function(err, customer) {
    if (err) {
    logError(err);
    fail(err);
    } else {
    done(customer.name);
    }
    }
    );
    }
    });
    }

    View Slide

  14. Async API ?

    View Slide

  15. Promise
    function fetchCustomerNameForOrder(orderId) {
    return fetchOrder(orderId)
    .then(order => fetchCustomer(order.customerId))
    .then(customer => customer.name);
    }

    View Slide

  16. .then(...)
    .then(...)
    .then(...)
    .then(...)
    .then(...)
    .then(...)
    .then(...)
    .then(...)

    View Slide

  17. Error
    Error
    Error
    Error

    View Slide

  18. function fetchCustomerNameForOrder(orderId) {
    return fetchOrder(orderId)
    .then(order => fetchCustomer(order.customerId))
    .then(customer => customer.name)
    .catch(err => {
    logError(err);
    throw err;
    });
    }

    View Slide

  19. Getting there…

    View Slide

  20. • More readable and
    maintainable
    • Synchronous-looking but
    nonblocking
    • Use native flow control
    constructs
    • More declarative and
    versatile

    View Slide

  21. async function fetchCustomerNameForOrder(orderId) {
    try {
    const order = await fetchOrder(orderId);
    const customer = await fetchCustomer(order.customerId);
    return customer.name;
    } catch (err) {
    logError(err);
    throw err;
    }
    }

    View Slide

  22. async function fetchCustomerNameForOrder(orderId) {
    try {
    const order = await fetchOrder(orderId);
    const customer = await fetchCustomer(order.customerId);
    return customer.name;
    } catch (err) {
    logError(err);
    throw err;
    }
    }

    View Slide

  23. async function fetchCustomerNameForOrder(orderId) {
    try {
    const order = await fetchOrder(orderId);
    const customer = await fetchCustomer(order.customerId);
    return customer.name;
    } catch (err) {
    logError(err);
    throw err;
    }
    }

    View Slide

  24. async function fetchCustomerNameForOrder(orderId) {
    try {
    const order = await fetchOrder(orderId);
    const customer = await fetchCustomer(order.customerId);
    return customer.name;
    } catch (err) {
    logError(err);
    throw err;
    }
    }

    View Slide

  25. Await

    View Slide

  26. function fetchOrder(orderId) {
    return fetch(`/orders/${orderId}`)
    .then(response => response.json());
    }
    function printOrder(orderId) {
    fetchOrder(orderId)
    .then(order => console.log(order));
    }

    View Slide

  27. function fetchOrder(orderId) {
    return fetch(`/orders/${orderId}`)
    .then(response => response.json());
    }
    async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }

    View Slide

  28. async printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    fetchOrder(orderId) order

    View Slide

  29. async printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    await

    View Slide

  30. async printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    await order

    View Slide

  31. await order

    View Slide

  32. await order

    View Slide

  33. async printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    } ?

    View Slide

  34. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);
    Invoke

    View Slide

  35. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);
    Encounter await

    View Slide

  36. async function printOrder(orderId) {
    const order = await fetchOrder(orderId);
    console.log(order);
    }
    printOrder(1);
    Wrap in Promise.resolve

    View Slide

  37. Wrap in Promise.resolve
    async function printOrder(orderId) {
    const promise = Promise.resolve(fetchOrder(orderId));
    console.log(order);
    }
    printOrder(1);

    View Slide

  38. async function printOrder(orderId) {
    const promise = Promise.resolve(fetchOrder(orderId));
    console.log(order);
    }
    printOrder(1);
    Wrap rest with then callback

    View Slide

  39. async function printOrder(orderId) {
    const promise = Promise.resolve(fetchOrder(orderId));
    return promise.then(order => {
    console.log(order);
    return Promise.resolve(undefined);
    });
    }
    printOrder(1);
    Wrap rest with then callback

    View Slide

  40. async function printOrder(orderId) {
    const promise = Promise.resolve(fetchOrder(orderId));
    return promise.then(order => {
    console.log(order);
    return Promise.resolve(undefined);
    });
    }
    printOrder(1);
    Wrap implicit return with
    Promise.resolve

    View Slide

  41. function fetchCustomerNameForOrder(orderId) {
    return Promise.resolve(fetchOrder(orderId))
    .then(order => {
    return Promise.resolve(fetchCustomer(order.customerId))
    .then(customer => {
    return Promise.resolve(customer.name);
    });
    });
    }
    ?

    View Slide

  42. async function meaningOfLife() {
    return 42;
    }
    meaningOfLife() === 42;
    ???

    View Slide

  43. async function meaningOfLife() {
    return 42;
    }
    function meaningOfLife() {
    return Promise.resolve(42);
    }
    meaningOfLife()
    .then(answer => answer === 42);

    View Slide

  44. Exceptional
    Situations

    View Slide

  45. async function printOrder(orderId) {
    try {
    const order = await fetchOrder(orderId);
    console.log(order);
    } catch (e) {
    console.log('Error retrieving order', e);
    }
    }

    View Slide

  46. async function printOrder(orderId) {
    try {
    const order = await fetchOrder(orderId);
    console.log(order);
    } catch (e) {
    console.log('Error retrieving order', e);
    }
    } ?

    View Slide

  47. function fetchOrder(orderId) {
    return fetch(`/orders/${orderId}`)
    .then(resp => {
    if (resp.status === 404) {
    throw new Error('Order not found');
    }
    return resp.json();
    });
    }

    View Slide

  48. function fetchOrder(orderId) {
    return new Promise((resolve, reject) => {
    $.get(`/orders/${orderId}`)
    .done(order => resolve(order))
    .fail(resp => {
    if (resp.status === 404) {
    reject(new Error('Order not found'));
    }
    });
    });
    }

    View Slide

  49. Error
    Error
    Error
    Error

    View Slide

  50. Regain Control

    View Slide

  51. async function findOrCreateOrder(orderId) {
    let order;
    if (await orderExists(orderId)) {
    order = await fetchOrder(orderId);
    } else {
    order = await createOrder();
    }
    return order;
    }

    View Slide

  52. async function findOrCreateOrder(orderId) {
    let order;
    if (await orderExists(orderId)) {
    order = await fetchOrder(orderId);
    } else {
    order = await createOrder();
    }
    return order;
    }

    View Slide

  53. async function printOrders(orderIds) {
    for (const id of orderIds) {
    const order = await fetchOrder(id);
    console.log(order);
    }
    }

    View Slide

  54. async function printOrders(orderIds) {
    for (const id of orderIds) {
    const order = await fetchOrder(id);
    console.log(order);
    }
    }

    View Slide

  55. async function printOrders(orderIds) {
    for (const id of orderIds) {
    const order = await fetchOrder(id);
    console.log(order);
    }
    } Problem?

    View Slide

  56. async function printOrders(orderIds) {
    for (const id of orderIds) {
    const order = await fetchOrder(id);
    console.log(order);
    }
    }
    printOrders([1, 2, 3]);

    View Slide

  57. printOrders([1, 2, 3]);
    await fetchOrder(1);
    await fetchOrder(2);
    await fetchOrder(3);

    View Slide

  58. printOrders([1, 2, 3]);
    await fetchOrder(1);
    await fetchOrder(2);
    await fetchOrder(3);

    View Slide

  59. printOrders([1, 2, 3]);
    await fetchOrder(1);
    await fetchOrder(2);
    await fetchOrder(3);

    View Slide

  60. Order is
    important

    View Slide

  61. async function printOrders(orderIds) {
    const orders = await Promise.all(
    orderIds.map(fetchOrder)
    );
    orders.forEach(order => console.log(order));
    }

    View Slide

  62. async function printOrders(orderIds) {
    const orders = await Promise.all(
    orderIds.map(fetchOrder)
    );
    orders.forEach(order => console.log(order));
    }

    View Slide

  63. printOrders([1, 2, 3]);
    await Promise.all([
    fetchOrder(1),
    fetchOrder(2),
    fetchOrder(3)
    ]);

    View Slide

  64. printOrders([1, 2, 3]);
    Demo
    rise.of.async.jeremyfairbank.com

    View Slide

  65. • Readable, synchronous-
    like code
    • Native flow control
    constructs
    • Sequential and
    concurrent processing

    View Slide

  66. • Readable, synchronous-
    like code
    • Native flow control
    constructs
    • Sequential and
    concurrent processing




    View Slide

  67. Events and
    streams?

    View Slide

  68. let counter = 0;
    function updateCounter(n) {
    counter += n;
    counterEl.innerHTML = counter;
    }
    incrementBtn.addEventListener('click', () => {
    updateCounter(1);
    });
    decrementBtn.addEventListener('click', () => {
    updateCounter(-1);
    });

    View Slide

  69. let counter = 0;
    function updateCounter(n) {
    counter += n;
    counterEl.innerHTML = counter;
    }
    incrementBtn.addEventListener('click', () => {
    updateCounter(1);
    });
    decrementBtn.addEventListener('click', () => {
    updateCounter(-1);
    });
    ×

    View Slide

  70. let counter = 0;
    async function listenIncrement() {
    await click(incrementBtn);
    counter += 1;
    counterEl.innerHTML = counter;
    listenIncrement();
    }

    View Slide

  71. let counter = 0;
    async function listenIncrement() {
    await click(incrementBtn);
    counter += 1;
    counterEl.innerHTML = counter;
    listenIncrement();
    }

    View Slide

  72. let counter = 0;
    async function listenIncrement() {
    await click(incrementBtn);
    counter += 1;
    counterEl.innerHTML = counter;
    listenIncrement();
    }

    View Slide

  73. let counter = 0;
    async function listenIncrement() {
    await click(incrementBtn);
    counter += 1;
    counterEl.innerHTML = counter;
    listenIncrement();
    }

    View Slide

  74. let counter = 0;
    async function listenIncrement() {
    await click(incrementBtn);
    counter += 1;
    counterEl.innerHTML = counter;
    listenIncrement();
    }
    ×

    View Slide

  75. const source = Observable.merge(
    Observable.fromEvent(incrementBtn, 'click').mapTo(1),
    Observable.fromEvent(decrementBtn, 'click').mapTo(-1),
    );
    source
    .scan((acc, curr) => acc + curr, 0)
    .subscribe((counter) => {
    counterEl.innerHTML = counter;
    });

    View Slide

  76. RxJS

    View Slide

  77. [ 1, 2, 3, 4, 5 ]
    Arrays
    Sequences in space

    View Slide

  78. Observables
    Sequences in time
    1 2 3 4 5

    View Slide

  79. Reactive

    View Slide

  80. Reactive
    3

    View Slide

  81. const { Observable } = require('rxjs');
    const source = Observable.of(1, 2, 3);
    source.subscribe(x => console.log(x));
    // 1
    // 2
    // 3

    View Slide

  82. const { Observable } = require('rxjs');
    const source = Observable.of(1, 2, 3);
    source.subscribe(x => console.log(x));
    // 1
    // 2
    // 3

    View Slide

  83. const { Observable } = require('rxjs');
    const source = Observable.of(1, 2, 3);
    source.subscribe(x => console.log(x));
    // 1
    // 2
    // 3

    View Slide

  84. const { Observable } = require('rxjs');
    const source = Observable.of(1, 2, 3);
    source.subscribe(x => console.log(x));
    // 1
    // 2
    // 3

    View Slide

  85. Observable.of(1, 2, 3)
    .subscribe(x => console.log(x));
    console.log
    subscribe

    View Slide

  86. Observable.of(1, 2, 3)
    .subscribe(x => console.log(x));
    console.log
    subscribe
    1

    View Slide

  87. Observable.of(1, 2, 3)
    .subscribe(x => console.log(x));
    console.log
    subscribe
    1

    View Slide

  88. Observable.of(1, 2, 3)
    .subscribe(x => console.log(x));
    console.log
    subscribe

    View Slide

  89. Observable.of(1, 2, 3)
    .subscribe(x => console.log(x));
    console.log
    subscribe
    2

    View Slide

  90. Observable.of(1, 2, 3)
    .subscribe(x => console.log(x));
    console.log
    subscribe
    2

    View Slide

  91. Observable.of(1, 2, 3)
    .subscribe(x => console.log(x));
    console.log
    subscribe

    View Slide

  92. Observable.of(1, 2, 3)
    .subscribe(x => console.log(x));
    console.log
    subscribe
    3

    View Slide

  93. Observable.of(1, 2, 3)
    .subscribe(x => console.log(x));
    console.log
    subscribe
    3

    View Slide

  94. Declarative
    Transformation
    Operate on events

    View Slide

  95. Observable.of(1, 2, 3)
    .map(n => n * 2)
    .subscribe(x => console.log(x));
    // 2
    // 4
    // 6

    View Slide

  96. Observable.of(1, 2, 3)
    .map(n => n * 2)
    .subscribe(x => console.log(x));
    // 2
    // 4
    // 6

    View Slide

  97. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe

    View Slide

  98. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe
    1

    View Slide

  99. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe
    1

    View Slide

  100. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe
    2

    View Slide

  101. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe
    2

    View Slide

  102. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe

    View Slide

  103. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe
    2

    View Slide

  104. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe
    2

    View Slide

  105. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe
    4

    View Slide

  106. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe
    4

    View Slide

  107. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe

    View Slide

  108. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe
    3

    View Slide

  109. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe
    3

    View Slide

  110. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe
    6

    View Slide

  111. Observable.of(1, 2, 3)
    console.log
    n * 2
    map subscribe
    6

    View Slide

  112. Lazy Transformation
    Do only as much work as
    needed

    View Slide

  113. Observable.range(1, 100)
    .map(n => n * 2)
    .filter(n => n > 4)
    .take(2)
    .subscribe(x => console.log(x));
    // 6
    // 8

    View Slide

  114. Observable.range(1, 100)
    .map(n => n * 2)
    .filter(n => n > 4)
    .take(2)
    .subscribe(x => console.log(x));
    // 6
    // 8

    View Slide

  115. Observable.range(1, 100)
    .map(n => n * 2)
    .filter(n => n > 4)
    .take(2)
    .subscribe(x => console.log(x));
    // 6
    // 8

    View Slide

  116. Observable.range(1, 100)
    .map(n => n * 2)
    .filter(n => n > 4)
    .take(2)
    .subscribe(x => console.log(x));
    // 6
    // 8

    View Slide

  117. Observable.range(1, 100)
    .map(n => n * 2)
    .filter(n => n > 4)
    .take(2)
    .subscribe(x => console.log(x));
    // 6
    // 8

    View Slide

  118. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  119. console.log
    n * 2
    map subscribe
    n > 4
    1
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  120. console.log
    n * 2
    map subscribe
    n > 4
    1
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  121. console.log
    n * 2
    map subscribe
    n > 4
    2
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  122. console.log
    n * 2
    map subscribe
    n > 4
    2
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  123. console.log
    n * 2
    map subscribe
    n > 4
    2
    Observable.range(1, 100)
    filter
    2
    take
    ×

    View Slide

  124. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  125. console.log
    n * 2
    map subscribe
    n > 4
    2
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  126. console.log
    n * 2
    map subscribe
    n > 4
    2
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  127. console.log
    n * 2
    map subscribe
    n > 4
    4
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  128. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    2
    take
    4

    View Slide

  129. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    2
    take
    4
    ×

    View Slide

  130. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  131. console.log
    n * 2
    map subscribe
    n > 4
    3
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  132. console.log
    n * 2
    map subscribe
    n > 4
    3
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  133. console.log
    n * 2
    map subscribe
    n > 4
    6
    Observable.range(1, 100)
    filter
    2
    take

    View Slide

  134. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    2
    take
    6

    View Slide


  135. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    2
    take
    6

    View Slide

  136. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    2
    take
    6

    View Slide

  137. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    1
    take
    6

    View Slide

  138. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    1
    take

    View Slide

  139. console.log
    n * 2
    map subscribe
    n > 4
    4
    Observable.range(1, 100)
    filter
    1
    take

    View Slide

  140. console.log
    n * 2
    map subscribe
    n > 4
    4
    Observable.range(1, 100)
    filter
    1
    take

    View Slide

  141. console.log
    n * 2
    map subscribe
    n > 4
    8
    Observable.range(1, 100)
    filter
    1
    take

    View Slide

  142. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    1
    take
    8

    View Slide


  143. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    1
    take
    8

    View Slide

  144. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    1
    take
    8

    View Slide

  145. console.log
    n * 2
    map subscribe
    n > 4
    Observable.range(1, 100)
    filter
    0
    take
    8

    View Slide

  146. DOM Events

    View Slide

  147. Observable.fromEvent(incrementBtn, 'click')
    .mapTo(1)
    .scan((acc, curr) => acc + curr, 0)
    .subscribe((counter) => {
    counterEl.innerHTML = counter;
    });

    View Slide

  148. Observable.fromEvent(incrementBtn, 'click')
    .mapTo(1)
    .scan((acc, curr) => acc + curr, 0)
    .subscribe((counter) => {
    counterEl.innerHTML = counter;
    });

    View Slide

  149. Observable.fromEvent(incrementBtn, 'click')
    .mapTo(1)
    .scan((acc, curr) => acc + curr, 0)
    .subscribe((counter) => {
    counterEl.innerHTML = counter;
    });

    View Slide

  150. Observable.fromEvent(incrementBtn, 'click')
    .mapTo(1)
    .scan((acc, curr) => acc + curr, 0)
    .subscribe((counter) => {
    counterEl.innerHTML = counter;
    });

    View Slide

  151. Observable.fromEvent(incrementBtn, 'click')
    .mapTo(1)
    .scan((acc, curr) => acc + curr, 0)
    .subscribe((counter) => {
    counterEl.innerHTML = counter;
    });

    View Slide

  152. Observable.fromEvent(incrementBtn, 'click')
    .mapTo(1)
    .scan((acc, curr) => acc + curr, 0)
    .subscribe((counter) => {
    counterEl.innerHTML = counter;
    });

    View Slide

  153. Observable.fromEvent(incrementBtn, 'click')
    .mapTo(1)
    .scan((acc, curr) => acc + curr, 0)
    .subscribe((counter) => {
    counterEl.innerHTML = counter;
    });

    View Slide

  154. .scan((acc, curr) => acc + curr, 0)
    Accumulated
    counter value

    View Slide

  155. .scan((acc, curr) => acc + curr, 0)
    Current event
    value (1)

    View Slide

  156. .scan((acc, curr) => acc + curr, 0)
    Return new accumulated
    counter value

    View Slide

  157. .scan((acc, curr) => acc + curr, 0)
    Initial counter
    value

    View Slide

  158. counterEl.innerHTML =
    counter
    acc + curr
    1
    mapTo subscribe
    0
    scan

    View Slide

  159. acc + curr
    1
    mapTo subscribe
    e
    scan
    0
    counterEl.innerHTML =
    counter

    View Slide

  160. 1
    mapTo subscribe
    e
    scan
    acc + curr
    0
    counterEl.innerHTML =
    counter

    View Slide

  161. 1
    mapTo subscribe
    1
    scan
    acc + curr
    0
    counterEl.innerHTML =
    counter

    View Slide

  162. 1
    mapTo subscribe
    scan
    1
    acc + curr
    0
    counterEl.innerHTML =
    counter

    View Slide

  163. 1
    mapTo subscribe
    scan
    1
    acc + curr
    1
    Counter value
    counterEl.innerHTML =
    counter

    View Slide

  164. 1
    mapTo subscribe
    scan
    1
    acc + curr
    1
    counterEl.innerHTML =
    counter

    View Slide

  165. acc + curr
    1
    mapTo subscribe
    scan
    1
    counterEl.innerHTML =
    counter

    View Slide

  166. acc + curr
    1
    mapTo subscribe
    e
    scan
    1
    counterEl.innerHTML =
    counter

    View Slide

  167. 1
    mapTo subscribe
    e
    scan
    acc + curr
    1
    counterEl.innerHTML =
    counter

    View Slide

  168. 1
    mapTo subscribe
    1
    scan
    acc + curr
    1
    counterEl.innerHTML =
    counter

    View Slide

  169. 1
    mapTo subscribe
    scan
    1
    acc + curr
    1
    counterEl.innerHTML =
    counter

    View Slide

  170. 1
    mapTo subscribe
    scan
    2
    acc + curr
    2
    Counter value
    counterEl.innerHTML =
    counter

    View Slide

  171. 1
    mapTo subscribe
    scan
    2
    acc + curr
    2
    counterEl.innerHTML =
    counter

    View Slide

  172. const source = Observable.merge(
    Observable.fromEvent(incrementBtn, 'click').mapTo(1),
    Observable.fromEvent(decrementBtn, 'click').mapTo(-1),
    );
    source
    .scan((acc, curr) => acc + curr, 0)
    .subscribe((counter) => {
    counterEl.innerHTML = counter;
    });

    View Slide

  173. const source = Observable.merge(
    Observable.fromEvent(incrementBtn, 'click').mapTo(1),
    Observable.fromEvent(decrementBtn, 'click').mapTo(-1),
    );
    source
    .scan((acc, curr) => acc + curr, 0)
    .subscribe((counter) => {
    counterEl.innerHTML = counter;
    });

    View Slide

  174. const source = Observable.merge(
    Observable.fromEvent(incrementBtn, 'click').mapTo(1),
    Observable.fromEvent(decrementBtn, 'click').mapTo(-1),
    );
    source
    .scan((acc, curr) => acc + curr, 0)
    .subscribe((counter) => {
    counterEl.innerHTML = counter;
    });

    View Slide

  175. const source = Observable.merge(
    Observable.fromEvent(incrementBtn, 'click').mapTo(1),
    Observable.fromEvent(decrementBtn, 'click').mapTo(-1),
    );
    source
    .scan((acc, curr) => acc + curr, 0)
    .subscribe((counter) => {
    counterEl.innerHTML = counter;
    });

    View Slide

  176. acc + curr
    1
    mapTo
    subscribe
    0
    scan
    -1
    mapTo
    Increment
    Decrement
    counterEl.innerHTML =
    counter

    View Slide

  177. acc + curr
    1
    mapTo
    subscribe
    0
    scan
    -1
    mapTo
    Increment
    Decrement
    e
    counterEl.innerHTML =
    counter

    View Slide

  178. acc + curr
    1
    mapTo
    subscribe
    0
    scan
    -1
    mapTo
    Increment
    Decrement
    e
    counterEl.innerHTML =
    counter

    View Slide

  179. acc + curr
    1
    mapTo
    subscribe
    0
    scan
    -1
    mapTo
    Increment
    Decrement
    1
    counterEl.innerHTML =
    counter

    View Slide

  180. acc + curr
    1
    mapTo
    subscribe
    0
    scan
    -1
    mapTo
    Increment
    Decrement
    1
    counterEl.innerHTML =
    counter

    View Slide

  181. acc + curr
    1
    mapTo
    subscribe
    1
    scan
    -1
    mapTo
    Increment
    Decrement
    1
    counterEl.innerHTML =
    counter

    View Slide

  182. acc + curr
    1
    mapTo
    subscribe
    1
    scan
    -1
    mapTo
    Increment
    Decrement
    1
    counterEl.innerHTML =
    counter

    View Slide

  183. acc + curr
    1
    mapTo
    subscribe
    1
    scan
    -1
    mapTo
    Increment
    Decrement
    counterEl.innerHTML =
    counter

    View Slide

  184. acc + curr
    1
    mapTo
    subscribe
    1
    scan
    -1
    mapTo
    Increment
    Decrement
    e
    counterEl.innerHTML =
    counter

    View Slide

  185. acc + curr
    1
    mapTo
    subscribe
    1
    scan
    -1
    mapTo
    Increment
    Decrement
    e
    counterEl.innerHTML =
    counter

    View Slide

  186. acc + curr
    1
    mapTo
    subscribe
    1
    scan
    -1
    mapTo
    Increment
    Decrement
    -1
    counterEl.innerHTML =
    counter

    View Slide

  187. acc + curr
    1
    mapTo
    subscribe
    1
    scan
    -1
    mapTo
    Increment
    Decrement
    -1
    counterEl.innerHTML =
    counter

    View Slide

  188. acc + curr
    1
    mapTo
    subscribe
    0
    scan
    -1
    mapTo
    Increment
    Decrement
    0
    counterEl.innerHTML =
    counter

    View Slide

  189. acc + curr
    1
    mapTo
    subscribe
    0
    scan
    -1
    mapTo
    Increment
    Decrement
    0
    counterEl.innerHTML =
    counter

    View Slide

  190. Async HTTP

    View Slide

  191. Promise
    fetchOrders()
    .then((orders) => {
    orders.forEach((order) => {
    console.log(order);
    });
    });

    View Slide

  192. Promise
    fetchOrders()
    .then((orders) => {
    orders.forEach((order) => {
    console.log(order);
    });
    });

    View Slide

  193. Observable
    fetchOrders()
    .subscribe((orders) => {
    orders.forEach((order) => {
    console.log(order);
    });
    });

    View Slide

  194. Why Observables?

    View Slide

  195. Promise
    fetchOrders()
    .then(orders => orders.filter(
    order => order.customerName === 'Tucker'
    ))
    .then(orders => orders.map(order => order.id))
    .then((orderIds) => {
    orderIds.forEach(id => console.log(id));
    });

    View Slide

  196. Promise
    fetchOrders()
    .then(orders => orders.filter(
    order => order.customerName === 'Tucker'
    ))
    .then(orders => orders.map(order => order.id))
    .then((orderIds) => {
    orderIds.forEach(id => console.log(id));
    });

    View Slide

  197. Promise
    fetchOrders()
    .then(orders => orders.filter(
    order => order.customerName === 'Tucker'
    ))
    .then(orders => orders.map(order => order.id))
    .then((orderIds) => {
    orderIds.forEach(id => console.log(id));
    });

    View Slide

  198. Promise
    fetchOrders()
    .then(orders => orders.filter(
    order => order.customerName === 'Tucker'
    ))
    .then(orders => orders.map(order => order.id))
    .then((orderIds) => {
    orderIds.forEach(id => console.log(id));
    });

    View Slide

  199. Async
    async function printCustomerNames() {
    const orders = await fetchOrders();
    const filtered = orders.filter(
    order => order.customerName === 'Tucker'
    );
    const orderIds = filtered.map(order => order.id);
    orderIds.forEach(id => console.log(id));
    }

    View Slide

  200. Async
    async function printCustomerNames() {
    const orders = await fetchOrders();
    const filtered = orders.filter(
    order => order.customerName === 'Tucker'
    );
    const orderIds = filtered.map(order => order.id);
    orderIds.forEach(id => console.log(id));
    }

    View Slide

  201. Async
    async function printCustomerNames() {
    const orders = await fetchOrders();
    const filtered = orders.filter(
    order => order.customerName === 'Tucker'
    );
    const orderIds = filtered.map(order => order.id);
    orderIds.forEach(id => console.log(id));
    }

    View Slide

  202. fetchOrders()
    .mergeAll()
    .filter(
    order => order.customerName === 'Tucker'
    )
    .map(order => order.id)
    .subscribe(id => console.log(id));
    Observable

    View Slide

  203. fetchOrders()
    .mergeAll()
    .filter(
    order => order.customerName === 'Tucker'
    )
    .map(order => order.id)
    .subscribe(id => console.log(id));
    Observable

    View Slide

  204. fetchOrders()
    .mergeAll()
    .filter(
    order => order.customerName === 'Tucker'
    )
    .map(order => order.id)
    .subscribe(id => console.log(id));
    Observable

    View Slide

  205. fetchOrders()
    .mergeAll()
    .filter(
    order => order.customerName === 'Tucker'
    )
    .map(order => order.id)
    .subscribe(id => console.log(id));
    Observable

    View Slide

  206. fetchOrders()
    .mergeAll()
    .filter(
    order => order.customerName === 'Tucker'
    )
    .map(order => order.id)
    .subscribe(id => console.log(id));
    Observable

    View Slide

  207. Cancellation

    View Slide

  208. const promise = fetchOrders()
    .then((orders) => {
    orders.foreach((order) => {
    console.log(order);
    });
    });
    promise.cancel();
    Promise

    View Slide

  209. const promise = fetchOrders()
    .then((orders) => {
    orders.foreach((order) => {
    console.log(order);
    });
    });
    promise.cancel();
    Promise

    View Slide

  210. const promise = fetchOrders()
    .then((orders) => {
    orders.foreach((order) => {
    console.log(order);
    });
    });
    promise.cancel();
    Promise
    ×

    View Slide

  211. Observable
    const subscription = fetchOrders()
    .subscribe((orders) => {
    orders.forEach((order) => {
    console.log(order);
    });
    });
    subscription.unsubscribe();

    View Slide

  212. Observable
    const subscription = fetchOrders()
    .subscribe((orders) => {
    orders.forEach((order) => {
    console.log(order);
    });
    });
    subscription.unsubscribe();

    View Slide

  213. console.log
    subscribe
    fetchOrders()
    .subscribe(...)

    View Slide

  214. console.log
    subscribe
    fetchOrders()
    .subscribe(...)

    View Slide

  215. subscription.unsubscribe();
    console.log
    subscribe
    ×
    fetchOrders()
    .subscribe(...)
    ×

    View Slide

  216. Create HTTP
    Requests

    View Slide

  217. function fetchOrders() {
    return Observable.ajax.get('/orders');
    }
    Built-in AJAX

    View Slide

  218. function fetchOrders() {
    return Observable.ajax.get('/orders');
    }
    Built-in AJAX

    View Slide

  219. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    Custom Observable
    Creation

    View Slide

  220. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    Custom Observable
    Creation

    View Slide

  221. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    Custom Observable
    Creation

    View Slide

  222. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    Custom Observable
    Creation

    View Slide

  223. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    Custom Observable
    Creation

    View Slide

  224. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    Custom Observable
    Creation

    View Slide

  225. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    Custom Observable
    Creation
    ?

    View Slide

  226. Observable.of(1, 2, 3)
    .subscribe(x => console.log(x));

    View Slide

  227. Observable.of(1, 2, 3)
    .subscribe(x => console.log(x));
    Subscriber

    View Slide

  228. Observable.of(1, 2, 3)
    .subscribe({
    next: x => console.log(x),
    });

    View Slide

  229. Observable.of(1, 2, 3)
    .subscribe({
    next: x => console.log(x),
    complete: () => console.log('Done!'),
    });
    // 1
    // 2
    // 3
    // Done!

    View Slide

  230. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    fetchOrders().mergeAll().subscribe({
    next: x => console.log(x),
    complete: () => console.log('Done!'),
    });

    View Slide

  231. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    fetchOrders().mergeAll().subscribe({
    next: x => console.log(x),
    complete: () => console.log('Done!'),
    });

    View Slide

  232. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    fetchOrders().mergeAll().subscribe({
    next: x => console.log(x),
    complete: () => console.log('Done!'),
    });

    View Slide

  233. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    fetchOrders().mergeAll()
    .subscribe(x => console.log(x));

    View Slide

  234. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    fetchOrders().mergeAll()
    .subscribe(x => console.log(x));
    No complete callback

    View Slide

  235. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    fetchOrders().mergeAll()
    .subscribe(x => console.log(x));
    Safe
    No complete callback

    View Slide

  236. const source = Observable.create((subscriber) => {
    subscriber.next(1);
    subscriber.error(new Error('Uh oh'));
    subscriber.next(2);
    });
    source.subscribe({
    next: x => console.log(x),
    complete: () => console.log('Done!'),
    error: e => console.error(e),
    });
    // 1
    // Error: Uh oh

    View Slide

  237. const source = Observable.create((subscriber) => {
    subscriber.next(1);
    subscriber.error(new Error('Uh oh'));
    subscriber.next(2);
    });
    source.subscribe({
    next: x => console.log(x),
    complete: () => console.log('Done!'),
    error: e => console.error(e),
    });
    // 1
    // Error: Uh oh

    View Slide

  238. const source = Observable.create((subscriber) => {
    subscriber.next(1);
    subscriber.error(new Error('Uh oh'));
    subscriber.next(2);
    });
    source.subscribe({
    next: x => console.log(x),
    complete: () => console.log('Done!'),
    error: e => console.error(e),
    });
    // 1
    // Error: Uh oh

    View Slide

  239. const source = Observable.create((subscriber) => {
    subscriber.next(1);
    subscriber.error(new Error('Uh oh'));
    subscriber.next(2);
    });
    source.subscribe({
    next: x => console.log(x),
    complete: () => console.log('Done!'),
    error: e => console.error(e),
    });
    // 1
    // Error: Uh oh
    Never called

    View Slide

  240. Error Handling

    View Slide

  241. Error
    Error
    Error
    Error
    Promises

    View Slide

  242. const source = Observable.create((subscriber) => {
    subscriber.next(1);
    subscriber.error(new Error('Uh oh'));
    subscriber.next(2);
    });
    source.subscribe({
    next: x => console.log(x),
    complete: () => console.log('Done!'),
    error: e => console.error(e),
    });

    View Slide

  243. const source = Observable.create((subscriber) => {
    subscriber.next(1);
    subscriber.error(new Error('Uh oh'));
    subscriber.next(2);
    });
    source.subscribe(x => console.log(x));
    ?

    View Slide

  244. const source = Observable.create((subscriber) => {
    subscriber.next(1);
    subscriber.error(new Error('Uh oh'));
    subscriber.next(2);
    });
    source.subscribe(x => console.log(x));

    View Slide

  245. fetchOrders()
    .catch((e) => {
    logError(e);
    return Observable.of([]);
    })
    .subscribe(x => console.log(x));

    View Slide

  246. fetchOrders()
    .catch((e) => {
    logError(e);
    return Observable.of([]);
    })
    .subscribe(x => console.log(x));

    View Slide

  247. fetchOrders()
    .catch((e) => {
    logError(e);
    return Observable.of([]);
    })
    .subscribe(x => console.log(x));

    View Slide

  248. fetchOrders()
    .catch((e) => {
    logError(e);
    return Observable.of([]);
    })
    .subscribe(x => console.log(x));

    View Slide

  249. fetchOrders()
    .catch((e) => {
    logError(e);
    return legacyFetchOrders()
    .catch((e2) => {
    logError(e2);
    return Observable.of([]);
    })
    })
    .subscribe(x => console.log(x));

    View Slide

  250. fetchOrders()
    .catch((e) => {
    logError(e);
    return legacyFetchOrders()
    .catch((e2) => {
    logError(e2);
    return Observable.of([]);
    })
    })
    .subscribe(x => console.log(x));

    View Slide

  251. fetchOrders()
    .catch((e) => {
    logError(e);
    return legacyFetchOrders()
    .catch((e2) => {
    logError(e2);
    return Observable.of([]);
    })
    })
    .subscribe(x => console.log(x));

    View Slide

  252. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])

    View Slide

  253. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])

    View Slide

  254. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])

    View Slide

  255. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])

    View Slide

  256. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])
    ×

    View Slide

  257. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])
    ×

    View Slide

  258. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])
    ×

    View Slide

  259. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])
    ×

    View Slide

  260. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])
    × ✓

    View Slide

  261. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])

    View Slide

  262. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])
    ×

    View Slide

  263. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])
    ×

    View Slide

  264. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])
    ×

    View Slide

  265. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])
    ×
    ×

    View Slide

  266. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])
    ×
    ×

    View Slide

  267. console.log
    catch subscribe
    legacyFetchOrders()
    fetchOrders()
    catch
    Observable.of([])
    ×
    ×
    []

    View Slide

  268. Clean Up

    View Slide

  269. function fetchOrdersStream() {
    return Observable.create((subscriber) => {
    const socket = new WebSocket('ws://example.com');
    socket.addEventListener('message', (event) => {
    subscriber.next(event.data);
    });
    return () => {
    socket.close();
    };
    });
    }

    View Slide

  270. function fetchOrdersStream() {
    return Observable.create((subscriber) => {
    const socket = new WebSocket('ws://example.com');
    socket.addEventListener('message', (event) => {
    subscriber.next(event.data);
    });
    return () => {
    socket.close();
    };
    });
    }

    View Slide

  271. function fetchOrdersStream() {
    return Observable.create((subscriber) => {
    const socket = new WebSocket('ws://example.com');
    socket.addEventListener('message', (event) => {
    subscriber.next(event.data);
    });
    return () => {
    socket.close();
    };
    });
    }

    View Slide

  272. function fetchOrdersStream() {
    return Observable.create((subscriber) => {
    const socket = new WebSocket('ws://example.com');
    socket.addEventListener('message', (event) => {
    subscriber.next(event.data);
    });
    return () => {
    socket.close();
    };
    });
    }
    Called when subscription
    unsubscribes

    View Slide

  273. function fetchOrdersStream() {
    return Observable.create((subscriber) => {
    const socket = new WebSocket('ws://example.com');
    socket.addEventListener('message', (event) => {
    subscriber.next(event.data);
    });
    return () => {
    socket.close();
    };
    });
    }
    Close socket,
    deallocate resources,
    etc.

    View Slide

  274. Lazy
    Subscriptions

    View Slide

  275. const p1 = fetchOrders();
    const p2 = fetchOrders();
    const p3 = fetchOrders();
    Promises

    View Slide

  276. const p1 = fetchOrders();
    const p2 = fetchOrders();
    const p3 = fetchOrders();
    Promises
    Immediate

    View Slide

  277. Observables
    const o1 = fetchOrders();
    const o2 = fetchOrders();
    const o3 = fetchOrders();
    o1.subscribe();
    o2.subscribe();
    o3.subscribe();

    View Slide

  278. Observables
    Lazy
    const o1 = fetchOrders();
    const o2 = fetchOrders();
    const o3 = fetchOrders();
    o1.subscribe();
    o2.subscribe();
    o3.subscribe();

    View Slide

  279. Observables
    Lazy
    const o1 = fetchOrders();
    const o2 = fetchOrders();
    const o3 = fetchOrders();
    o1.subscribe();
    o2.subscribe();
    o3.subscribe();
    Issue Request

    View Slide

  280. const o1 = fetchOrders();
    o1.subscribe();
    o1.subscribe();
    o1.subscribe();

    View Slide

  281. const o1 = fetchOrders();
    o1.subscribe();
    o1.subscribe();
    o1.subscribe();
    Pure

    View Slide

  282. const o1 = fetchOrders();
    o1.subscribe();
    o1.subscribe();
    o1.subscribe();
    Side Effects
    Pure

    View Slide

  283. Hot Cold
    vs.

    View Slide

  284. Observable
    creates the source
    Cold

    View Slide

  285. Best for one-off
    unique requests.
    Cold

    View Slide

  286. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }

    View Slide

  287. function fetchOrders() {
    return Observable.create((subscriber) => {
    fetchOrdersFromDb((orders) => {
    subscriber.next(orders);
    subscriber.complete();
    });
    });
    }
    Resource requested/created
    at subscription time

    View Slide

  288. Hot
    Observable
    closes over the source

    View Slide

  289. Hot
    Best for multicasting
    and sharing resources.

    View Slide

  290. const socket = new WebSocket('ws://example.com');
    const stream = Observable.create((subscriber) => {
    socket.addEventListener('message', (event) => {
    subscriber.next(event.data);
    });
    });

    View Slide

  291. const socket = new WebSocket('ws://example.com');
    const stream = Observable.create((subscriber) => {
    socket.addEventListener('message', (event) => {
    subscriber.next(event.data);
    });
    });
    Resource created outside
    subscription

    View Slide

  292. const socket = new WebSocket('ws://example.com');
    const stream = Observable.create((subscriber) => {
    socket.addEventListener('message', (event) => {
    subscriber.next(event.data);
    });
    });
    Close over existing resource
    when subscribing

    View Slide

  293. const sub1 = stream.subscribe(x => console.log(x));
    const sub2 = stream.subscribe(x => console.log(x));

    View Slide

  294. const sub1 = stream.subscribe(x => console.log(x));
    const sub2 = stream.subscribe(x => console.log(x));
    Shared stream of data

    View Slide

  295. fetchOrders()
    .mergeAll()
    .filter(
    order => order.customerName === 'Tucker'
    )
    .map(order => order.id)
    .subscribe(id => console.log(id));
    Recall

    View Slide

  296. fetchOrders()
    .mergeAll()
    .filter(
    order => order.customerName === 'Tucker'
    )
    .map(order => order.id)
    .subscribe(id => console.log(id));
    ?
    Recall

    View Slide

  297. Observable.of([1, 2, 3])
    .subscribe(x => console.log(x));
    // [ 1, 2, 3 ]

    View Slide

  298. Observable.of([1, 2, 3])
    .subscribe(x => console.log(x));
    // [ 1, 2, 3 ]

    View Slide

  299. Observable.of([1, 2, 3])
    .subscribe(x => console.log(x));
    // [ 1, 2, 3 ]

    View Slide

  300. Observable.of([1, 2, 3])
    .mergeAll()
    .subscribe(x => console.log(x));
    // 1
    // 2
    // 3

    View Slide

  301. Observable.of([1, 2, 3])
    .mergeAll()
    .subscribe(x => console.log(x));
    // 1
    // 2
    // 3
    Flatten

    View Slide

  302. Observable.of([1, 2, 3])
    .mergeAll()
    .subscribe(x => console.log(x));
    // 1
    // 2
    // 3

    View Slide

  303. subscribe
    mergeAll
    console.log
    [1, 2, 3]

    View Slide

  304. subscribe
    mergeAll
    console.log
    [1, 2, 3]

    View Slide

  305. subscribe
    mergeAll
    console.log
    [2, 3] 1

    View Slide

  306. subscribe
    mergeAll
    console.log
    [2, 3] 1

    View Slide

  307. subscribe
    mergeAll
    console.log
    [3] 2

    View Slide

  308. subscribe
    mergeAll
    console.log
    [3] 2

    View Slide

  309. subscribe
    mergeAll
    console.log
    3

    View Slide

  310. subscribe
    mergeAll
    console.log
    3

    View Slide

  311. Higher Order
    Observables

    View Slide

  312. Observable.of(1, 2, 3)
    .map(n => n * 2)
    .subscribe(x => console.log(x));
    // 2
    // 4
    // 6

    View Slide

  313. Observable.of(1, 2, 3)
    .map(n => n * 2)
    .subscribe(x => console.log(x));
    // 2
    // 4
    // 6
    Delay?

    View Slide

  314. Observable.of(1, 2, 3)
    .map(n => Observable.of(n * 2))
    .subscribe(x => console.log(x));
    // ScalarObservable { value: 2 }
    // ScalarObservable { value: 4 }
    // ScalarObservable { value: 6 }

    View Slide

  315. Observable.of(1, 2, 3)
    .map(n => Observable.of(n * 2))
    .subscribe(x => console.log(x));
    // ScalarObservable { value: 2 }
    // ScalarObservable { value: 4 }
    // ScalarObservable { value: 6 }

    View Slide

  316. Observable.of(1, 2, 3)
    .map(n => Observable.of(n * 2))
    .subscribe(x => console.log(x));
    // ScalarObservable { value: 2 }
    // ScalarObservable { value: 4 }
    // ScalarObservable { value: 6 }

    View Slide

  317. Observable.of(1, 2, 3)
    .map(n => Observable.of(n * 2))
    .mergeAll()
    .subscribe(x => console.log(x));
    // 2
    // 4
    // 6

    View Slide

  318. Observable.of(1, 2, 3)
    .map(n => Observable.of(n * 2))
    .mergeAll()
    .subscribe(x => console.log(x));
    // 2
    // 4
    // 6

    View Slide

  319. Observable.of(1, 2, 3)
    .mergeMap(n => Observable.of(n * 2))
    .subscribe(x => console.log(x));
    // 2
    // 4
    // 6

    View Slide

  320. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)

    View Slide

  321. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    1

    View Slide

  322. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    1

    View Slide

  323. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    Observable.of(2)
    subscribe
    2

    View Slide

  324. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    Observable.of(2)
    subscribe
    2

    View Slide

  325. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    Observable.of(2)
    subscribe
    2

    View Slide

  326. Observable.of(1)
    .delay(1000)
    .subscribe(x => console.log(x))
    //
    // 1

    View Slide

  327. Observable.of(1)
    .delay(1000)
    .subscribe(x => console.log(x))
    //
    // 1

    View Slide

  328. Observable.of(1)
    .delay(1000)
    .subscribe(x => console.log(x))
    //
    // 1

    View Slide

  329. Observable.of(1)
    .delay(1000)
    .subscribe(x => console.log(x))
    //
    // 1

    View Slide

  330. Observable.of(1, 2, 3)
    .mergeMap(n => (
    Observable.of(n * 2).delay(1000)
    ))
    .subscribe(x => console.log(x));
    //
    // 2
    // 4
    // 6

    View Slide

  331. Observable.of(1, 2, 3)
    .mergeMap(n => (
    Observable.of(n * 2).delay(1000)
    ))
    .subscribe(x => console.log(x));
    //
    // 2
    // 4
    // 6

    View Slide

  332. Observable.of(1, 2, 3)
    .mergeMap(n => (
    Observable.of(n * 2).delay(1000)
    ))
    .subscribe(x => console.log(x));
    //
    // 2
    // 4
    // 6

    View Slide

  333. Observable.of(1, 2, 3)
    .mergeMap(n => (
    Observable.of(n * 2).delay(1000)
    ))
    .subscribe(x => console.log(x));
    //
    // 2
    // 4
    // 6
    ?

    View Slide

  334. Concurrency

    View Slide

  335. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)

    View Slide

  336. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    1

    View Slide

  337. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    1

    View Slide

  338. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    2

    View Slide

  339. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    2
    2

    View Slide

  340. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    2
    2

    View Slide

  341. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    2
    4

    View Slide

  342. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    3
    2
    4

    View Slide

  343. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    3
    2
    4

    View Slide

  344. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    2
    4
    6

    View Slide

  345. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    2
    4
    6

    View Slide

  346. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    4
    6
    2

    View Slide

  347. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    6
    4

    View Slide

  348. subscribe
    mergeMap
    console.log
    Observable.of(n * 2)
    6

    View Slide

  349. Observable.of(1, 2, 3)
    .mergeMap(n => (
    Observable.of(n * 2).delay(1000)
    ), 1)
    .subscribe(x => console.log(x));
    //
    // 2
    //
    // 4
    //
    // 6

    View Slide

  350. Observable.of(1, 2, 3)
    .mergeMap(n => (
    Observable.of(n * 2).delay(1000)
    ), 1)
    .subscribe(x => console.log(x));
    //
    // 2
    //
    // 4
    //
    // 6

    View Slide

  351. Observable.of(1, 2, 3)
    .mergeMap(n => (
    Observable.of(n * 2).delay(1000)
    ), 1)
    .subscribe(x => console.log(x));
    //
    // 2
    //
    // 4
    //
    // 6

    View Slide

  352. Observable.of(1, 2, 3)
    .mergeMap(n => (
    Observable.of(n * 2).delay(1000)
    ), 1)
    .subscribe(x => console.log(x));
    //
    // 2
    //
    // 4
    //
    // 6

    View Slide

  353. Observable.of(1, 2, 3)
    .mergeMap(n => (
    Observable.of(n * 2).delay(1000)
    ), 1)
    .subscribe(x => console.log(x));
    //
    // 2
    //
    // 4
    //
    // 6

    View Slide

  354. Observable.of(1, 2, 3)
    .concatMap(n => (
    Observable.of(n * 2).delay(1000)
    ))
    .subscribe(x => console.log(x));
    //
    // 2
    //
    // 4
    //
    // 6

    View Slide

  355. subscribe
    concatMap
    console.log
    Observable.of(n * 2)

    View Slide

  356. subscribe
    console.log
    Observable.of(n * 2)
    1
    concatMap

    View Slide

  357. subscribe
    console.log
    Observable.of(n * 2)
    1
    concatMap

    View Slide

  358. subscribe
    console.log
    Observable.of(n * 2)
    2
    concatMap

    View Slide

  359. subscribe
    console.log
    Observable.of(n * 2)
    2
    concatMap

    View Slide

  360. subscribe
    console.log
    Observable.of(n * 2)
    2
    concatMap

    View Slide

  361. subscribe
    console.log
    Observable.of(n * 2)
    2
    concatMap

    View Slide

  362. subscribe
    console.log
    Observable.of(n * 2)
    2
    concatMap

    View Slide

  363. subscribe
    console.log
    Observable.of(n * 2)
    4
    concatMap

    View Slide

  364. subscribe
    console.log
    Observable.of(n * 2)
    4
    concatMap

    View Slide

  365. subscribe
    console.log
    Observable.of(n * 2)
    4
    concatMap

    View Slide

  366. subscribe
    console.log
    Observable.of(n * 2)
    3
    concatMap

    View Slide

  367. subscribe
    console.log
    Observable.of(n * 2)
    3
    concatMap

    View Slide

  368. subscribe
    console.log
    Observable.of(n * 2)
    6
    concatMap

    View Slide

  369. subscribe
    console.log
    Observable.of(n * 2)
    6
    concatMap

    View Slide

  370. subscribe
    console.log
    Observable.of(n * 2)
    6
    concatMap

    View Slide

  371. Rate Limiting

    View Slide

  372. Observable.of(1, 2, 3)
    .concatMap((id) => {
    const url = `/orders/${id}`;
    return Observable.ajax.get(url)
    .delay(1000);
    })
    .pluck('response')
    .bufferCount(3)
    .subscribe(x => console.log(x));
    // [ { id: '1', name: 'Order 1' },
    // { id: '2', name: 'Order 2' },
    // { id: '3', name: 'Order 3' } ]

    View Slide

  373. Observable.of(1, 2, 3)
    .concatMap((id) => {
    const url = `/orders/${id}`;
    return Observable.ajax.get(url)
    .delay(1000);
    })
    .pluck('response')
    .bufferCount(3)
    .subscribe(x => console.log(x));
    // [ { id: '1', name: 'Order 1' },
    // { id: '2', name: 'Order 2' },
    // { id: '3', name: 'Order 3' } ]

    View Slide

  374. Observable.of(1, 2, 3)
    .concatMap((id) => {
    const url = `/orders/${id}`;
    return Observable.ajax.get(url)
    .delay(1000);
    })
    .pluck('response')
    .bufferCount(3)
    .subscribe(x => console.log(x));
    // [ { id: '1', name: 'Order 1' },
    // { id: '2', name: 'Order 2' },
    // { id: '3', name: 'Order 3' } ]

    View Slide

  375. Observable.of(1, 2, 3)
    .concatMap((id) => {
    const url = `/orders/${id}`;
    return Observable.ajax.get(url)
    .delay(1000);
    })
    .pluck('response')
    .bufferCount(3)
    .subscribe(x => console.log(x));
    // [ { id: '1', name: 'Order 1' },
    // { id: '2', name: 'Order 2' },
    // { id: '3', name: 'Order 3' } ]

    View Slide

  376. Observable
    .ajax
    .get(url)
    subscribe
    console.log
    pluck bufferCount
    'response' 3
    concatMap

    View Slide

  377. Observable
    .ajax
    .get(url)
    subscribe
    concatMap
    console.log
    pluck bufferCount
    'response' 3
    1

    View Slide

  378. Observable
    .ajax
    .get(url)
    subscribe
    concatMap
    console.log
    pluck bufferCount
    'response' 3

    View Slide

  379. Observable
    .ajax
    .get(url)
    subscribe
    concatMap
    console.log
    pluck bufferCount
    'response' 3
    R
    Rate limit next request

    View Slide

  380. Observable
    .ajax
    .get(url)
    subscribe
    concatMap
    console.log
    pluck bufferCount
    'response' 3
    R

    View Slide

  381. Observable
    .ajax
    .get(url)
    subscribe
    concatMap
    console.log
    pluck bufferCount
    'response' 3
    R

    View Slide

  382. Observable
    .ajax
    .get(url)
    subscribe
    concatMap
    console.log
    pluck bufferCount
    'response' 3
    O1

    View Slide

  383. Observable
    .ajax
    .get(url)
    subscribe
    concatMap
    console.log
    pluck bufferCount
    'response' 2
    O1

    View Slide

  384. Observable
    .ajax
    .get(url)
    subscribe
    concatMap
    console.log
    pluck bufferCount
    'response' 2
    O1
    +

    View Slide

  385. Observable
    .ajax
    .get(url)
    subscribe
    concatMap
    console.log
    pluck bufferCount
    'response' 1
    O1
    O2

    View Slide

  386. Observable
    .ajax
    .get(url)
    subscribe
    concatMap
    console.log
    pluck bufferCount
    'response' 1
    O1
    O2
    +

    View Slide

  387. Observable
    .ajax
    .get(url)
    subscribe
    concatMap
    console.log
    pluck bufferCount
    'response' 0
    O1
    O2
    O3

    View Slide

  388. Observable
    .ajax
    .get(url)
    subscribe
    concatMap
    console.log
    […]
    pluck bufferCount
    'response' 0

    View Slide

  389. Sequential vs.
    Parallel

    View Slide

  390. const promise = Promise.all([
    fetchOrder(1),
    fetchOrder(2),
    fetchOrder(3),
    ]);
    promise.then(x => console.log(x));
    // [ { id: '1', name: 'Order 1' },
    // { id: '2', name: 'Order 2' },
    // { id: '3', name: 'Order 3' } ]

    View Slide

  391. const promise = Promise.all([
    fetchOrder(1),
    fetchOrder(2),
    fetchOrder(3),
    ]);
    promise.then(x => console.log(x));
    // [ { id: '1', name: 'Order 1' },
    // { id: '2', name: 'Order 2' },
    // { id: '3', name: 'Order 3' } ]

    View Slide

  392. const source = Observable.forkJoin(
    fetchOrder(1),
    fetchOrder(2),
    fetchOrder(3)
    );
    source.subscribe(x => console.log(x));
    // [ { response: { id: 1, name: 'Order 1' } },
    // { response: { id: 2, name: 'Order 2' } },
    // { response: { id: 3, name: 'Order 3' } } ]

    View Slide

  393. const source = Observable.forkJoin(
    fetchOrder(1),
    fetchOrder(2),
    fetchOrder(3)
    );
    source.subscribe(x => console.log(x));
    // [ { response: { id: 1, name: 'Order 1' } },
    // { response: { id: 2, name: 'Order 2' } },
    // { response: { id: 3, name: 'Order 3' } } ]

    View Slide

  394. const source = Observable.forkJoin(
    fetchOrder(1),
    fetchOrder(2),
    fetchOrder(3)
    );
    source.subscribe(x => console.log(x));
    // [ { response: { id: 1, name: 'Order 1' } },
    // { response: { id: 2, name: 'Order 2' } },
    // { response: { id: 3, name: 'Order 3' } } ]
    Observable or
    Promise

    View Slide

  395. fetchOrder(1)
    fetchOrder(2)
    fetchOrder(3)
    forkJoin subscribe
    console.log

    View Slide

  396. fetchOrder(1)
    fetchOrder(2)
    fetchOrder(3)
    forkJoin subscribe
    console.log
    R1

    View Slide

  397. fetchOrder(1)
    fetchOrder(2)
    fetchOrder(3)
    forkJoin
    R1
    subscribe
    console.log

    View Slide

  398. fetchOrder(1)
    fetchOrder(2)
    fetchOrder(3)
    forkJoin
    R1
    subscribe
    console.log
    R3

    View Slide

  399. fetchOrder(1)
    fetchOrder(2)
    fetchOrder(3)
    forkJoin
    R1
    R3
    subscribe
    console.log

    View Slide

  400. fetchOrder(1)
    fetchOrder(2)
    fetchOrder(3)
    forkJoin
    R1
    R3
    subscribe
    console.log
    R2

    View Slide

  401. fetchOrder(1)
    fetchOrder(2)
    fetchOrder(3)
    forkJoin
    R1
    R2
    R3
    subscribe
    console.log

    View Slide

  402. fetchOrder(1)
    fetchOrder(2)
    fetchOrder(3)
    forkJoin subscribe
    console.log
    R1
    R2
    R3

    View Slide

  403. • Declarative, lazy
    operations
    • Expressive event
    management
    • No more error swallowing
    • Rate limiting and
    concurrent processing

    View Slide

  404. • Declarative, lazy
    operations
    • Expressive event
    management
    • No more error swallowing
    • Rate limiting and
    concurrent processing




    View Slide

  405. Resources

    View Slide

  406. Browser compatibility:

    kangax.github.io/compat-table/es2016plus
    Node compatibility:

    node.green
    Async

    View Slide

  407. Async
    babeljs.io

    View Slide

  408. Observables
    github.com/ReactiveX/rxjs
    RxJS

    View Slide

  409. Observables
    ECMAScript Proposal:

    github.com/tc39/proposal-observable
    Another spec implementation:

    github.com/zenparsing/zen-observable

    View Slide

  410. THANKS!
    Jeremy Fairbank
    blog.jeremyfairbank.com
    @elpapapollo | gh/jfairbank
    bit.ly/devnexus-rise-of-async
    SLIDES:
    github.com/jfairbank/rise-of-async-js-talk
    CODE:

    View Slide