JazzCon 2017: The Rise of Async JavaScript

JazzCon 2017: The Rise of Async JavaScript

94bd558238b69c45d3d3e15797ae94f7?s=128

Jeremy Fairbank

March 23, 2017
Tweet

Transcript

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

  2. sigient.com

  3. Async API Callback

  4. Callback fetchUserById(1, function(err, user) { if (err) { console.error('Could not

    retrieve user'); } else { console.log(user); } });
  5. }); }); }); }); }); }); Callback Callback Callback Callback

    Callback Callback
  6. 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); } } ); } }); }
  7. 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); } } ); } }); }
  8. 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); } } ); } }); }
  9. Async API ?

  10. Promise function fetchCustomerNameForOrder(orderId) { return fetchOrder(orderId) .then(order => fetchCustomer(order.customerId)) .then(customer

    => customer.name); }
  11. .then(...) .then(...) .then(...) .then(...) .then(...) .then(...) .then(...) .then(...)

  12. Error Error Error Error

  13. function fetchCustomerNameForOrder(orderId) { return fetchOrder(orderId) .then(order => fetchCustomer(order.customerId)) .then(customer =>

    customer.name) .catch(err => { logError(err); throw err; }); }
  14. Getting there…

  15. • More readable and maintainable • Synchronous-looking but nonblocking •

    Use native flow control constructs • More declarative and versatile
  16. 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; } }
  17. 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; } }
  18. 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; } }
  19. 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; } }
  20. Await

  21. function fetchOrder(orderId) { return fetch(`/orders/${orderId}`) .then(response => response.json()); } function

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

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

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

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

    await order
  26. await order

  27. await order

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

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

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

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

    } printOrder(1); Wrap in Promise.resolve
  32. Wrap in Promise.resolve async function printOrder(orderId) { const promise =

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

    printOrder(1); Wrap rest with then callback
  34. 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
  35. 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
  36. async function meaningOfLife() { return 42; } meaningOfLife() === 42;

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

    return Promise.resolve(42); } meaningOfLife() .then(answer => answer === 42);
  38. Exceptional Situations

  39. async function printOrder(orderId) { try { const order = await

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

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

    === 404) { throw new Error('Order not found'); } return resp.json(); }); }
  42. 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')); } }); }); }
  43. Error Error Error Error

  44. Regain Control

  45. async function findOrCreateOrder(orderId) { let order; if (await orderExists(orderId)) {

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

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

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

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

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

    const order = await fetchOrder(id); console.log(order); } } printOrders([1, 2, 3]);
  51. printOrders([1, 2, 3]); await fetchOrder(1); await fetchOrder(2); await fetchOrder(3);

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

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

  54. Order is important

  55. async function printOrders(orderIds) { const orders = await Promise.all( orderIds.map(fetchOrder)

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

    ); orders.forEach(order => console.log(order)); }
  57. printOrders([1, 2, 3]); await Promise.all([ fetchOrder(1), fetchOrder(2), fetchOrder(3) ]);

  58. • Readable, synchronous- like code • Native flow control constructs

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

    • Sequential and concurrent processing ✓ ✓ ✓ ✓
  60. Events and streams?

  61. let counter = 0; function updateCounter(n) { counter += n;

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

    counterEl.innerHTML = counter; } incrementBtn.addEventListener('click', () => { updateCounter(1); }); decrementBtn.addEventListener('click', () => { updateCounter(-1); }); ×
  63. 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; });
  64. RxJS

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

    space
  66. Observables Sequences in time 1 2 3 4 5

  67. Reactive

  68. Reactive 3

  69. const { Observable } = require('rxjs'); const source = Observable.of(1,

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

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

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

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

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

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

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

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

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

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

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

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

  82. Declarative Transformation Operate on events

  83. Observable.of(1, 2, 3) .map(n => n * 2) .subscribe(x =>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  100. Lazy Transformation Do only as much work as needed

  101. Observable.range(1, 100) .map(n => n * 2) .filter(n => n

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

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

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

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

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

    100) filter 2 take
  107. console.log n * 2 map subscribe n > 4 1

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

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

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

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

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

    100) filter 2 take
  113. console.log n * 2 map subscribe n > 4 2

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

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

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

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

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

    100) filter 2 take
  119. console.log n * 2 map subscribe n > 4 3

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

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

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

    100) filter 2 take 6
  123. ✓ console.log n * 2 map subscribe n > 4

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

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

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

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

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

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

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

    100) filter 1 take 8
  131. ✓ console.log n * 2 map subscribe n > 4

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

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

    100) filter 0 take 8
  134. DOM Events

  135. Observable.fromEvent(incrementBtn, 'click') .mapTo(1) .scan((acc, curr) => acc + curr, 0)

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

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

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

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

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

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

    .subscribe((counter) => { counterEl.innerHTML = counter; });
  142. .scan((acc, curr) => acc + curr, 0) Accumulated counter value

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

    (1)
  144. .scan((acc, curr) => acc + curr, 0) Return new accumulated

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

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

    scan
  147. acc + curr 1 mapTo subscribe e scan 0 counterEl.innerHTML

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

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

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

    = counter
  151. 1 mapTo subscribe scan 1 acc + curr 1 Counter

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

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

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

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

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

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

    = counter
  158. 1 mapTo subscribe scan 2 acc + curr 2 Counter

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

    = counter
  160. 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; });
  161. 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; });
  162. 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; });
  163. 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; });
  164. acc + curr 1 mapTo subscribe 0 scan -1 mapTo

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

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

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

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

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

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

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

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

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

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

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

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

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

    Increment Decrement 0 counterEl.innerHTML = counter
  178. Async HTTP

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

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

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

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

    }); Lazy unlike Promise
  183. Why Observables?

  184. 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)); });
  185. 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)); });
  186. 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)); });
  187. 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)); });
  188. 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)); }
  189. 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)); }
  190. 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)); }
  191. fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order

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

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

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

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

    => order.id) .subscribe(id => console.log(id)); Observable
  196. Cancellation

  197. const promise = fetchOrders() .then((orders) => { orders.foreach((order) => {

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

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

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

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

    console.log(order); }); }); subscription.unsubscribe(); Observable Cancel request
  202. Create HTTP Requests

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

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

  205. function fetchOrders() { return Observable.create((subscriber) => { fetchOrdersFromDb((orders) => {

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

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

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

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

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

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

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

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

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

  215. Observable.of(1, 2, 3) .subscribe({ next: x => console.log(x), complete: ()

    => console.log('Done!'), }); // 1 // 2 // 3 // Done!
  216. 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
  217. 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
  218. 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
  219. 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
  220. Error Handling

  221. Error Error Error Error Promises

  222. 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), });
  223. const source = Observable.create((subscriber) => { subscriber.next(1); subscriber.error(new Error('Uh oh'));

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

    subscriber.next(2); }); source.subscribe(x => console.log(x));
  225. fetchOrders() .catch((e) => { logError(e); return Observable.of([]); }) .subscribe(x =>

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

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

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

    console.log(x));
  229. fetchOrders() .mergeAll() .filter( order => order.customerName === 'Tucker' ) .map(order

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

    => order.id) .subscribe(id => console.log(id)); ? Recall
  231. Observable.of([1, 2, 3]) .subscribe(x => console.log(x)); // [ 1, 2,

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

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

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

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

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

    2 // 3
  237. subscribe mergeAll console.log [1, 2, 3]

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

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

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

  241. subscribe mergeAll console.log [3] 2

  242. subscribe mergeAll console.log [3] 2

  243. subscribe mergeAll console.log 3

  244. subscribe mergeAll console.log 3

  245. Higher Order Observables

  246. Observable.of(1, 2, 3) .map(n => n * 2) .subscribe(x =>

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

    console.log(x)); // 2 // 4 // 6 Delay?
  248. 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 }
  249. 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 }
  250. 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 }
  251. Observable.of(1, 2, 3) .map(n => Observable.of(n * 2)) .mergeAll() .subscribe(x

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

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

    console.log(x)); // 2 // 4 // 6
  254. subscribe mergeMap console.log Observable.of(n * 2)

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

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

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

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

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

  260. Observable.of(1) .delay(1000) .subscribe(x => console.log(x)) // <tick> // 1

  261. Observable.of(1) .delay(1000) .subscribe(x => console.log(x)) // <tick> // 1

  262. Observable.of(1) .delay(1000) .subscribe(x => console.log(x)) // <tick> // 1

  263. Observable.of(1) .delay(1000) .subscribe(x => console.log(x)) // <tick> // 1

  264. Observable.of(1, 2, 3) .mergeMap(n => ( Observable.of(n * 2).delay(1000) ))

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

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

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

    .subscribe(x => console.log(x)); // <tick> // 2 // 4 // 6 ?
  268. Concurrency

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  283. 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
  284. 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
  285. 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
  286. 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
  287. 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
  288. Observable.of(1, 2, 3) .concatMap(n => ( Observable.of(n * 2).delay(1000) ))

    .subscribe(x => console.log(x)); // <tick> // 2 // <tick> // 4 // <tick> // 6
  289. subscribe concatMap console.log Observable.of(n * 2)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  305. Rate Limiting

  306. 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' } ]
  307. 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' } ]
  308. 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' } ]
  309. 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' } ]
  310. Observable .ajax .get(url) subscribe console.log pluck bufferCount 'response' 3 concatMap

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

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

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

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

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

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

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

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

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

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

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

    O1 O2 O3
  322. Observable .ajax .get(url) subscribe concatMap console.log […] pluck bufferCount 'response'

    0
  323. • Declarative, lazy operations • Expressive event management • No

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

    more error swallowing • Rate limiting and concurrent processing ✓ ✓ ✓ ✓
  325. Resources

  326. Browser compatibility:
 kangax.github.io/compat-table/es2016plus Node compatibility:
 node.green Async

  327. Async babeljs.io

  328. Observables github.com/ReactiveX/rxjs RxJS

  329. Observables ECMAScript Proposal:
 github.com/tc39/proposal-observable Another spec implementation:
 github.com/zenparsing/zen-observable

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