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

ATL JS: Dive into RxJS Observables

ATL JS: Dive into RxJS Observables

Jeremy Fairbank

November 27, 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 async code • Better error

    handling • More declarative and versatile syntax • Capable of handling events, streams, and HTTP
  5. import { Observable } from 'rxjs'; const source = Observable.of(1,

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Observable.range(1, 100) filter 1 take 8
  30. let counter = 0; function updateCounter(n) { counter += n;

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

    counterEl.innerHTML = counter; } incrementBtn.addEventListener('click', () => { updateCounter(1); }); decrementBtn.addEventListener('click', () => { updateCounter(-1); });
  32. Observable.fromEvent(incrementBtn, 'click') .mapTo(1) .scan((acc, curr) => acc + curr, 0)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Increment Decrement 0 counterEl.innerHTML = counter
  55. 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)); });
  56. 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)); });
  57. 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)); });
  58. 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)); });
  59. fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order

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

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

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

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

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

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

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

    console.log(order); }); }); promise.cancel(); Promise ×
  67. const subscription = fetchOrders() .subscribe((orders) => { orders.forEach((order) => {

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

    console.log(order); }); }); subscription.unsubscribe(); Observable Cancel request
  69. Observables const o1 = fetchOrders(); const o2 = fetchOrders(); const

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

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

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

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

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

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

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

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

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

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

    => console.log('Done!'), }); // 1 // 2 // 3 // Done!
  80. function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {

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

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

    subscriber.next(orders); subscriber.complete(); }); }); } fetchOrders().subscribe({ next: x => console.log(x), complete: () => console.log('Done!'), });
  83. 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
  84. 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
  85. 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
  86. const source = Observable.create((subscriber) => { subscriber.next(1); subscriber.error(new Error('Uh oh'));

    subscriber.next(2); }); source.subscribe(x => console.log(x)); No error handler, so what happens?
  87. const source = Observable.create((subscriber) => { subscriber.next(1); subscriber.error(new Error('Uh oh'));

    subscriber.next(2); }); source.subscribe(x => console.log(x)); No swallowed errors!
  88. 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
  89. 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
  90. fetchOrders() .catch((e) => { logError(e); return legacyFetchOrders() .catch((e2) => {

    logError(e2); return Observable.of([]); }) }) .subscribe(x => console.log(x)); Or delegate to legacy API…
  91. fetchOrders() .catch((e) => { logError(e); return legacyFetchOrders() .catch((e2) => {

    logError(e2); return Observable.of([]); }) }) .subscribe(x => console.log(x)); Or delegate to legacy API…
  92. fetchOrders() .catch((e) => { logError(e); return legacyFetchOrders() .catch((e2) => {

    logError(e2); return Observable.of([]); }) }) .subscribe(x => console.log(x)); Or delegate to legacy API…
  93. fetchOrders() .catch((e) => { logError(e); return legacyFetchOrders() .catch((e2) => {

    logError(e2); return Observable.of([]); }) }) .subscribe(x => console.log(x)); Or delegate to legacy API…
  94. function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {

    subscriber.next(orders); subscriber.complete(); }); }); } Resource requested/created at subscription time
  95. function ordersStream() { return Observable.create((subscriber) => { const url =

    'ws://example.com/orders'; const socket = new WebSocket(url); socket.addEventListener('message', (data) => { subscriber.next(data); }); }); }
  96. function ordersStream() { return Observable.create((subscriber) => { const url =

    'ws://example.com/orders'; const socket = new WebSocket(url); socket.addEventListener('message', (data) => { subscriber.next(data); }); }); }
  97. function ordersStream() { return Observable.create((subscriber) => { const url =

    'ws://example.com/orders'; const socket = new WebSocket(url); socket.addEventListener('message', (data) => { subscriber.next(data); }); }); }
  98. function ordersStream() { return Observable.create((subscriber) => { const url =

    'ws://example.com/orders'; const socket = new WebSocket(url); socket.addEventListener('message', (data) => { subscriber.next(data); }); }); }
  99. const url = 'ws://example.com/orders'; const socket = new WebSocket(url); function

    ordersStream() { return Observable.create((subscriber) => { socket.addEventListener('message', (data) => { subscriber.next(data); }); }); }
  100. const url = 'ws://example.com/orders'; const socket = new WebSocket(url); function

    ordersStream() { return Observable.create((subscriber) => { socket.addEventListener('message', (data) => { subscriber.next(data); }); }); } Resource created outside subscription
  101. const url = 'ws://example.com/orders'; const socket = new WebSocket(url); function

    ordersStream() { return Observable.create((subscriber) => { socket.addEventListener('message', (data) => { subscriber.next(data); }); }); } Close over existing resource when subscribing
  102. Shared stream of data const stream = ordersStream(); const sub1

    = stream.subscribe(x => console.log(x)); const sub2 = stream.subscribe(x => console.log(x));
  103. function ordersStream() { return Observable.create((subscriber) => { const url =

    'ws://example.com/orders'; const socket = new WebSocket(url); socket.addEventListener('message', (data) => { subscriber.next(data); }); }) .share(); } Share It
  104. function ordersStream() { return Observable.create((subscriber) => { const url =

    'ws://example.com/orders'; const socket = new WebSocket(url); socket.addEventListener('message', (data) => { subscriber.next(data); }); }) .share(); } Share It
  105. function ordersStream() { return Observable.create((subscriber) => { const url =

    'ws://example.com/orders'; const socket = new WebSocket(url); socket.addEventListener('message', (data) => { subscriber.next(data); }); }) .share(); } Share It
  106. Shared stream of data too const stream = ordersStream(); const

    sub1 = stream.subscribe(x => console.log(x)); const sub2 = stream.subscribe(x => console.log(x));
  107. function ordersStream() { return Observable.create((subscriber) => { const url =

    'ws://example.com/orders'; const socket = new WebSocket(url); socket.addEventListener('message', (data) => { subscriber.next(data); }); return () => { socket.close(); }; }) .share(); }
  108. function ordersStream() { return Observable.create((subscriber) => { const url =

    'ws://example.com/orders'; const socket = new WebSocket(url); socket.addEventListener('message', (data) => { subscriber.next(data); }); return () => { socket.close(); }; }) .share(); } Called when all subscriptions unsubscribe
  109. function ordersStream() { return Observable.create((subscriber) => { const url =

    'ws://example.com/orders'; const socket = new WebSocket(url); socket.addEventListener('message', (data) => { subscriber.next(data); }); return () => { socket.close(); }; }) .share(); } Close socket, deallocate resources, etc.
  110. fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order

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

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

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

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

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

    .subscribe(x => console.log(x)); // <tick> // 2 // 4 // 6 ?
  119. 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
  120. 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
  121. 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
  122. 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
  123. 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
  124. Observable.of(1, 2, 3) .concatMap(n => ( Observable.of(n * 2).delay(1000) ))

    .subscribe(x => console.log(x)); // <tick> // 2 // <tick> // 4 // <tick> // 6
  125. 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' } ]
  126. 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' } ]
  127. 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' } ]
  128. 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' } ]
  129. 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' } ]
  130. 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' } ]
  131. 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' } } ]
  132. 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' } } ]
  133. 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
  134. • Declarative, lazy operations • Expressive event management • No

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

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