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

Exploring Async Techniques in Javascript

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

Exploring Async Techniques in Javascript

Avatar for Guilherme Coelho

Guilherme Coelho

January 24, 2018
Tweet

Other Decks in Technology

Transcript

  1. grvcoelho @ pagarme Hello! • Guilherme Coelho • Tech Leader

    @ pagarme • https://github.com/grvcoelho 2
  2. grvcoelho @ pagarme func main() { fmt.Println("BEGIN") contents, _ :=

    ioutil.ReadFile("/tmp/dat") fmt.Print(string(contents)) fmt.Println("FINISH") } 3
  3. grvcoelho @ pagarme const fs = require('fs') console.log('BEGIN') fs.readFile('/tmp/dat', (err,

    contents) => { console.log(String(contents)) }) console.log('FINISH') 4
  4. grvcoelho @ pagarme Async • Part of your program runs

    now • Part of your program runs later 5
  5. grvcoelho @ pagarme Featuring • Callbacks • Promises • Generators

    + Coroutines • Async/Await • Reactive Extensions 6
  6. grvcoelho @ pagarme Callbacks • Node.js is built on top

    of them • Fundamental unit of asynchrony in Javascript • Pass a function to another function that will call it back 7
  7. grvcoelho @ pagarme const fs = require('fs') console.log('BEGIN') readFile('/tmp/dat', (err,

    contents) => { console.log(String(contents)) }) console.log('FINISH') 8
  8. grvcoelho @ pagarme const readFile = (filename, callback) => {

    system.openAndReadFile(filename, (err, data) => { if (err) { return callback(err, null) } callback(null, String(data)) }) } readFile('/tmp/dat', (err, contents) => { console.log(contents) }) 9
  9. grvcoelho @ pagarme const divide = (a, b, callback) =>

    { if (b === 0) { return callback('Cannot be zero', null) } const result = a / b callback(null, result) } divide(10, 2, (err, result) => { // ... }) 10
  10. grvcoelho @ pagarme runA(function () { runB() runC(function () {

    runD() }) runE() }) runF() // A -> F -> B -> C -> E -> D 11
  11. grvcoelho @ pagarme runA(function () { // sync runB() runC(function

    () { // sync runD() }) runE() }) runF() // A -> B -> C -> D -> E -> F 12
  12. grvcoelho @ pagarme const finishOrder = (orderId, callback) => {

    getProducts(orderId, (err, products) => { getStock(products, (err, hasStock) => { calculateTotal(products, (err, total) => { getShipping((err, shipping) => { confirmPurchase(orderId, shipping, (err, message) => { callback(null, message) }) }) }) }) }) } finishOrder(123, (err, message) => { // ... }) 13
  13. grvcoelho @ pagarme Callback Hell • Indentation* • Poor error

    handling • Asynchronous flow in nonlinear, nonsequantial way • Inversion of control 14
  14. grvcoelho @ pagarme async.waterfall([ function(callback) { callback(null, 'one', 'two') },

    function(arg1, arg2, callback) { // arg1 now equals 'one' and arg2 now equals 'two' callback(null, 'three') }, function(arg1, callback) { // arg1 now equals 'three' callback(null, 'done') }, ], function (err, result) { // result now equals 'done' }) 17
  15. grvcoelho @ pagarme Callback Problems • Sync and async callbacks

    • Error handling + Error swallowing • Calling the callback multiple times • Never calling the callback 19
  16. grvcoelho @ pagarme Promises • A representation of a future

    value • Time-independent container/wrapper of a value • The Hamburger Analogy 20
  17. grvcoelho @ pagarme buyHamburger() .then(hamburger => { return eatHamburger(hamburger) })

    .catch(err => { console.log(err) return goHomeChateado() }) 21
  18. grvcoelho @ pagarme Promises • Have 3 states: Pending, Fulfilled,

    Rejected • The state never changes from Fulfilled or Rejected • Can only be resolved once 22
  19. grvcoelho @ pagarme Promises • Promises are chainable • Promises

    are always async • Every .then call returns a new Promise • Rejected values go until they find a .catch 24
  20. grvcoelho @ pagarme const finishOrder = (orderId, callback) => {

    getProducts(orderId, (err, products) => { getStock(products, (err, hasStock) => { calculateTotal(products, (err, total) => { getShipping((err, shipping) => { confirmPurchase(orderId, shipping, (err, message) => { callback(null, message) }) }) }) }) }) } finishOrder(123, (err, message) => { // ... }) 25
  21. grvcoelho @ pagarme const finishOrder = (orderId) => { return

    getProducts(orderId) .then(getStock) .then(calculateTotal) .then(getShipping) .then(confirmPurchase) .catch(handleErrors) } finishOrder(123) .then(message => { displayThankYouPage() }) 26
  22. grvcoelho @ pagarme const calculateTotal = (product, quantity) => {

    return new Promise((resolve, reject) => { fetch(‘/${product.id}/price', (err, price) => { if (err) { return reject(err) } const total = price * quantity return resolve(total) }) }) } 27
  23. grvcoelho @ pagarme Promise vs Callback Problems • Sync and

    async callbacks • Error handling + Error swallowing • Calling the callback multiple times • Never calling the callback 29
  24. grvcoelho @ pagarme Promise.resolve(5) .then(x => { return x *

    2 }) .then(result => { console.log(result) return anotherPromise() }) console.log(‘Hello!') 31 Promise.resolve
  25. grvcoelho @ pagarme Promise.resolve(5) .then(x => { if (x ===

    5) { return Promise.reject( new Error('Value not allowed’) ) } else if (Number.isNaN(Number(x))) { throw new TypeError('Must be a number') } }) .catch(err => { console.log(err.message) }) 32 Promise.reject
  26. grvcoelho @ pagarme const timeout = (ms) => new Promise((resolve,

    reject) => { setTimeout( () => reject(new TimeoutError()), ms ) }) Promise.race([ timeout(3000), chargeCreditCard() ]) .then(displaySuccess) .catch(handleTimeout) 34
  27. grvcoelho @ pagarme findOrder(123) .then(order => { return findItems(order) })

    .then(items => { return findCustomer(order) // no order here }) 36 Problems with scope
  28. grvcoelho @ pagarme findOrder(123) .then(order => { return findItems(order) .then(items

    => { return findCustomer(order) .then(customer => { return chargeOrder(order, items, customer) }) }) }) 37 Problems with scope
  29. grvcoelho @ pagarme findOrder(123) .then(order => { return Promise.all([order, findItems(order)])

    }) .then(([order, items]) => { return Promise.all([ order, items, findCustomer(order) ]) }) .then(([order, items, customer]) => { return chargeOrder(order, items, customer) }) 38 Problems with scope
  30. grvcoelho @ pagarme const handleOrder = (orderId) => { const

    order = findOrder(orderId) const items = findItems(order) const customer = findCustomer(order) return chargeOrder(order, items, customer) } 39
  31. grvcoelho @ pagarme const handleOrder = orderId => coroutine(function*() {

    const order = yield findOrder(orderId) const items = yield findItems(order) const customer = yield findCustomer(order) return chargeOrder(order, items, customer) }) 40
  32. grvcoelho @ pagarme Generators • Supported since Node 4.8.x •

    Make it possible to pause and resume function execution • Change of control between generator and iterator 41
  33. grvcoelho @ pagarme function* gen () { console.log('Inside generator') yield

    'Press next' const name = yield return `Hello ${name}` } const it = gen() it.next() // { done: false, value: 'Press next' } it.next() // { done: false, value: undefined } it.next(‘Gui') // { done: true, value: "Hello Gui" } 42
  34. grvcoelho @ pagarme Coroutines • Use generators to “stop” functions

    • Resume functions when promises are resolved • Write async code that flows like sync 43
  35. grvcoelho @ pagarme const p = coroutine(function* () { const

    a = yield Promise.resolve(5) const b = yield (a * 2) return a + b // 15 }) 44
  36. grvcoelho @ pagarme const coroutine = (generator) => { const

    it = generator() const next = (data) => { const message = it.next(data) if (!message.done) { return Promise.resolve(message.value) .then(next) } return Promise.resolve(message.value) } return next() } 45
  37. grvcoelho @ pagarme const handleOrder = orderId => coroutine(function*() {

    const order = yield findOrder(orderId) const items = yield findItems(order) const customer = yield findCustomer(order) return chargeOrder(order, items, customer) }) 46
  38. grvcoelho @ pagarme const handleOrder = order => coroutine(function* ()

    { const items = yield findItems(order) const customer = yield findCustomer(order) return chargeOrder(order, items, customer) }) // vs const handleOrder = (order) => { return Promise.all([ findItems(order), findCustomer(order) ]) .then(([items, customer]) => { return chargeOrder(order, items, customer) }) } 47
  39. grvcoelho @ pagarme const handleOrder = coroutine(function* (orderId) { const

    order = yield findOrder(orderId) const [items, customer] = yield Promise.all([ findItems(order), findCustomer(order) ]) return chargeOrder(order, items, customer) }) 48
  40. grvcoelho @ pagarme Coroutines @ pagarme • Used in some

    controllers and services • Mostly used on tests • Specially used in e2e tests 49
  41. grvcoelho @ pagarme let response before(() => { return request(‘/orders').then(response

    => { response = response }) }) it('should test request', () => { assert(response.status).is(200) }) // vs it('should test request', coroutine(function* () { const response = yield request('/orders') assert(response.status).is(200) })) 50
  42. grvcoelho @ pagarme Async/Await • Supported since Node 7.6.x •

    Works just like Generators + Coroutines • Is built on top of Promises, returns Promises • Write async code that flows like sync 51
  43. grvcoelho @ pagarme const handleOrder = coroutine(function* (orderId) { const

    order = yield findOrder(orderId) const items = yield findItems(order) const customer = yield findCustomer(order) return chargeOrder(order, items, customer) }) 52
  44. grvcoelho @ pagarme const handleOrder = async function (orderId) {

    const order = await findOrder(orderId) const items = await findItems(order) const customer = await findCustomer(order) return chargeOrder(order, items, customer) } 53
  45. grvcoelho @ pagarme const fn = async function () {

    const values = [2, 5] const result = values.map(async function (x) { return await double(x) }) return result // [Promise{}, Promise{}] } 54
  46. grvcoelho @ pagarme const fn = async function () {

    const values = [2, 5] const promises = values.map(x => double(x)) const result = await Promise.all(promises) return result // [4, 10] } 55
  47. grvcoelho @ pagarme Reactive Programming • Asynchronous streams of events/data

    • Intervals, webSockets, DOM events • Single vs Multiple values • Cancellable vs Uncancellable • Eager vs lazy 56
  48. grvcoelho @ pagarme const numberPromise = new Promise((resolve, reject) =>

    { resolve(42) resolve(100) }) numberPromise.then(value => console.log(value)) 57
  49. grvcoelho @ pagarme const numberObservable = Observable.create((observer) => { observer.next(42)

    observer.error() observer.completed() }) numberObservable.subscribe(value => console.log(value)) 58
  50. grvcoelho @ pagarme const Rx = require('rx') const observable =

    Rx.Observable.interval(1000) .map(x => x + 1) .takeWhile(x => x <= 3) .concat(Rx.Observable.of('World')) observable.subscribe(x => console.log(`Hello ${x}!`)) // Hello 1 // Hello 2 // Hello 3 // Hello World 59
  51. grvcoelho @ pagarme const clickStream = Rx.Observable.fromEvent( button, ‘click’ )

    const multiClickStream = clickStream .buffer(() => clickStream.throttle(250)) .map((list) => list.length) .filter(len => len >= 2) .subscribe(() => alert(‘Double Click!’)) 60
  52. grvcoelho @ pagarme Conclusions • Don’t use callbacks* • Promises

    “resolve” almost all your problems. Use it by default • Use coroutines when you can’t use async/ await • Use async/await to resolve some scope issues • Reactive Programming is worth your attention 61