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

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. 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); } } ); } }); }
  2. 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); } } ); } }); }
  3. 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); } } ); } }); }
  4. • More readable and maintainable • Synchronous-looking but nonblocking •

    Use native flow control constructs • More declarative and versatile
  5. 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; } }
  6. 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; } }
  7. 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; } }
  8. 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; } }
  9. function fetchOrder(orderId) { return fetch(`/orders/${orderId}`) .then(response => response.json()); } function

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

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

    Promise.resolve(fetchOrder(orderId)); console.log(order); } printOrder(1);
  12. 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
  13. 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
  14. async function meaningOfLife() { return 42; } function meaningOfLife() {

    return Promise.resolve(42); } meaningOfLife() .then(answer => answer === 42);
  15. async function printOrder(orderId) { try { const order = await

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

    fetchOrder(orderId); console.log(order); } catch (e) { console.log('Error retrieving order', e); } } ?
  17. function fetchOrder(orderId) { return fetch(`/orders/${orderId}`) .then(resp => { if (resp.status

    === 404) { throw new Error('Order not found'); } return resp.json(); }); }
  18. 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')); } }); }); }
  19. async function findOrCreateOrder(orderId) { let order; if (await orderExists(orderId)) {

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

    order = await fetchOrder(orderId); } else { order = await createOrder(); } return order; }
  21. async function printOrders(orderIds) { for (const id of orderIds) {

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

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

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

    const order = await fetchOrder(id); console.log(order); } } printOrders([1, 2, 3]);
  25. • Readable, synchronous- like code • Native flow control constructs

    • Sequential and concurrent processing ✓ ✓ ✓ ✓
  26. let counter = 0; function updateCounter(n) { counter += n;

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

    counterEl.innerHTML = counter; } incrementBtn.addEventListener('click', () => { updateCounter(1); }); decrementBtn.addEventListener('click', () => { updateCounter(-1); }); ×
  28. let counter = 0; async function listenIncrement() { await click(incrementBtn);

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

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

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

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

    counter += 1; counterEl.innerHTML = counter; listenIncrement(); } ×
  33. const { Observable } = require('rxjs'); const source = Observable.of(1,

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

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

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

    2, 3); source.subscribe(x => console.log(x)); // 1 // 2 // 3
  37. Observable.range(1, 100) .map(n => n * 2) .filter(n => n

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

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

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

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

    > 4) .take(2) .subscribe(x => console.log(x)); // 6 // 8
  42. console.log n * 2 map subscribe n > 4 1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Observable.range(1, 100) filter 1 take 8
  58. Observable.fromEvent(incrementBtn, 'click') .mapTo(1) .scan((acc, curr) => acc + curr, 0)

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

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

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

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

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

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

    .subscribe((counter) => { counterEl.innerHTML = counter; });
  65. 1 mapTo subscribe scan 1 acc + curr 1 Counter

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

    value counterEl.innerHTML = counter
  67. acc + curr 1 mapTo subscribe 0 scan -1 mapTo

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

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

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

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

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

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

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

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

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

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

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

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

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

    Increment Decrement 0 counterEl.innerHTML = counter
  81. 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)); });
  82. 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)); });
  83. 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)); });
  84. 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)); });
  85. 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)); }
  86. 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)); }
  87. 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)); }
  88. fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order

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

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

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

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

    => order.id) .subscribe(id => console.log(id)); Observable
  93. const promise = fetchOrders() .then((orders) => { orders.foreach((order) => {

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

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

    console.log(order); }); }); promise.cancel(); Promise ×
  96. function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {

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

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

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

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

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

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

    subscriber.next(orders); subscriber.complete(); }); }); } Custom Observable Creation ?
  103. Observable.of(1, 2, 3) .subscribe({ next: x => console.log(x), complete: ()

    => console.log('Done!'), }); // 1 // 2 // 3 // Done!
  104. 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!'), });
  105. 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!'), });
  106. 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!'), });
  107. function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {

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

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

    subscriber.next(orders); subscriber.complete(); }); }); } fetchOrders().mergeAll() .subscribe(x => console.log(x)); Safe No complete callback
  110. 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
  111. 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
  112. 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
  113. 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
  114. 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), });
  115. fetchOrders() .catch((e) => { logError(e); return legacyFetchOrders() .catch((e2) => {

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

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

    logError(e2); return Observable.of([]); }) }) .subscribe(x => console.log(x));
  118. function fetchOrdersStream() { return Observable.create((subscriber) => { const socket =

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

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

    new WebSocket('ws://example.com'); socket.addEventListener('message', (event) => { subscriber.next(event.data); }); return () => { socket.close(); }; }); }
  121. 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
  122. 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.
  123. Observables const o1 = fetchOrders(); const o2 = fetchOrders(); const

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

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

    const o3 = fetchOrders(); o1.subscribe(); o2.subscribe(); o3.subscribe(); Issue Request
  126. function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {

    subscriber.next(orders); subscriber.complete(); }); }); } Resource requested/created at subscription time
  127. const socket = new WebSocket('ws://example.com'); const stream = Observable.create((subscriber) =>

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

    { socket.addEventListener('message', (event) => { subscriber.next(event.data); }); }); Resource created outside subscription
  129. 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
  130. fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order

    => order.id) .subscribe(id => console.log(id)); ? Recall
  131. Observable.of(1, 2, 3) .map(n => n * 2) .subscribe(x =>

    console.log(x)); // 2 // 4 // 6 Delay?
  132. 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 }
  133. 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 }
  134. 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 }
  135. Observable.of(1, 2, 3) .mergeMap(n => ( Observable.of(n * 2).delay(1000) ))

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

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

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

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

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

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

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

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

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

    .subscribe(x => console.log(x)); // <tick> // 2 // <tick> // 4 // <tick> // 6
  145. 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' } ]
  146. 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' } ]
  147. 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' } ]
  148. 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' } ]
  149. 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' } ]
  150. 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' } ]
  151. 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' } } ]
  152. 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' } } ]
  153. 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
  154. • Declarative, lazy operations • Expressive event management • No

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

    more error swallowing • Rate limiting and concurrent processing ✓ ✓ ✓ ✓