Async & Await

James Hunter

@cccc00

Warning To Functional People

So many side effects


  ::console.log

  console.log.bind(console)

  Promise.resolve(4)
    .then(::console.log)
    // 4

Works Like

=

Some friends...


  function bob (x) {
    return x + x
  }

  function bob (x) {
    return x + x
  }

bob is reliable


  function bob (x) {
    return x + x
  }

bob is predictable


  function bob (x) {
    return x + x
  }

bob always answers arguments the same way


  function bob (x) {
    return x + x
  }

bob is a singular synchronous operation


  function steve (x) {
    const y = inc()
    return x + y
  }

  function steve (x) {
    const y = inc()
    return x + y
  }

steve is nice


  function steve (x) {
    const y = inc()
    return x + y
  }

steve has a second job on the side


  function steve (x) {
    const y = inc()
    return x + y
  }

steves second job blocks him from

doing his actual job


  function steve (x) {
    const y = inc()
    return x + y
  }

steve is a singular synchronous operation


  function cam (x, cb) {
    inc((err, y) => {
      cb(err, x + y)
    })
  }

cam.


  function cam (x, cb) {
    inc((err, y) => {
      cb(err, x + y)
    })
  }

cam...


  function cam (x, cb) {
    inc((err, y) => {
      cb(err, x + y)
    })
  }

cam replies right away


  function cam (x, cb) {
    inc((err, y) => {
      cb(err, x + y)
    })
  }

cam is all over the place


  function cam (x, cb) {
    inc((err, y) => {
      cb(err, x + y)
    })
  }

cam  likes rules, not tools


  function cam (x, cb) {
    inc((err, y) => {
      cb(err, x + y)
    })
  }

cam is a singular asynchronous operation


  function cam (x, cb) {
    inc((err, y) => {
      cb(err, x + y)
    })
  }

  function pat (x) {
    return inc()
      .then(y => x + y)
      .catch(::console.error)
  }

  function pat (x) {
    return inc()
      .then(y => x + y)
      .catch(::console.error)
  }

pat (like cam) replies right away


  function pat (x) {
    return inc()
      .then(y => x + y)
      .catch(::console.error)
  }

pat looks simple


  function pat (x) {
    return inc()
      .then(y => x + y)
      .catch(::console.error)
  }

pat thinks differently than you


  function pat (x) {
    return inc()
      .then(y => x + y)
      .catch(::console.error)
  }

pat is a singular asynchronous operation

I know what you are thinking

I know what you are thinking

Functions aren't people

get off the stage

I know what you are thinking

Functions aren't people

But in all seriousness

Why does this stuff

have to be so complicated

much synchronous

very asynchronous

such promises

many callbacks

Why cant I just write it

like a normal function

such synchronous

much asynchronous

many promises

very callbacks

Like steve...

very synchronous

many asynchronous

much promises

such callbacks


  function steve (x) {
    const y = inc()
    return x + y
  }

good ol' steve


  function steve (x) {
    const y = inc()
    return x + y
  }

I want to write my asynchronous code like this


  function steve (x) {
    const y = inc()
    return x + y
  }

I want a super steve


  function steve (x) {
    const y = inc()
    return x + y
  }

I want a super steve

– Marvel

How close can we get to a

singular asynchronous

version of steve?


  function steve (x) {
    const y = inc()
    return x + y
  }

  import { coroutine } from "bluebird"

  let superSteve = coroutine(function * (x) {
    const y = yield inc()
    return x + y
  })

Turns out pretty close


  function steve (x) {
    const y = inc()
    return x + y
  }

  import { coroutine } from "bluebird"

  let superSteve = coroutine(function * (x) {
    const y = yield inc()
    return x + y
  })

Turns out pretty close


  let steve = function (x) {
    const y = inc()
    return x + y
  }

What is this black magic

how is it doing this


  import { coroutine } from "bluebird"

  let superSteve = coroutine(function * (x) {
    const y = yield inc()
    return x + y
  })

well....

3 things main points

1 – Iterators

1 – Iterators

are synchronous sequences, where the consumer pulls data at the rate they desire

– Domenic Denicola


  let iter = ["a","b","c"].entries()

  iter.next() // {value: [0,"a"], done: false}
  iter.next() // {value: [1,"b"], done: false}
  iter.next() // {value: [2,"c"], done: false}
  iter.next() // {done: true}
  iter.next() // {done: true}

2 – Generators

2 – Generators

"Basic" use of generator functions is to simply yield values and create a sequence, sending values out of the generator function (through the iterable) at the rate the consumer desires.

– Domenic Denicola


  function * bananas () {
    while (1) {
      yield "banana"
    }
  }

  function take (n, iter) {
    let result = []
    while (result.length < n) {
      result.push(iter.next().value)
    }
    return result
  }

  take(2, bananas())
  // ["banana", "banana"]

3 - Generators (again)

3 - Generators (again)

Generators also have an "advanced" mode, where you can send values into the generator function

– Domenic Denicola


  function double (genFn) {
    let iter  = genFn()
    let value = 0
    let done  = false

    while (!done) {
      const next = iter.next(value * 2)
      done  = next.done
      value = next.value
    }
    return value
  }

  double(function * () {
    const a = yield 1
    const b = yield 2
    return a + b
  }) // 6

  import { coroutine } from "bluebird"

  let superSteve = coroutine(function * (x) {
    const y = yield inc()
    return x + y
  })

coroutine (spawn) is a function that 

inverts the normal consumer-producer

model of the generator to create iterable sequences

The Hack Is...


  import { coroutine } from "bluebird"

  let superSteve = coroutine(function * (x) {
    const y = yield inc()
    return x + y
  })

  function steve (x) {
    const y = inc()
    return x + y
  }

But... Can we get even closer to steve?


  async function superSteve (x) {
    const y = await inc()
    return x + y
  }

  function steve (x) {
    const y = inc()
    return x + y
  }

  async function superSteve (x) {
    const y = await inc()
    return x + y
  }

  function steve (x) {
    const y = inc()
    return x + y
  }

  async function superSteve (x) {
    const y = await inc()
    return x + y
  }
  • usable today with transpilers like babel (stage 1)


  async function superSteve (x) {
    const y = await inc()
    return x + y
  }
  • usable today with transpilers like babel (stage 1)

  • declaratively say that we are going to wait on inc


  async function superSteve (x) {
    const y = await inc()
    return x + y
  }
  • usable today with transpilers like babel (stage 1)

  • declaratively say that we are going to wait on inc

  • the generator stuff is an implementation detail


  async function superSteve (x) {
    const y = await inc()
    return x + y
  }
  • usable today with transpilers like babel (stage 1)

  • declaratively say that we are going to wait on inc

  • the generator stuff is an implementation detail

  • returns a promise

  async function superSteve (x) {
    const y = await inc()
    return x + y
  }

  async steveception (x) {
    const a = await superSteve(x)
    const b = await superSteve(a)
    const c = await superSteve(b)
    return a + b + c
  }
  • usable today with transpilers like babel (stage 1)

  • declaratively say that we are going to wait on inc

  • the generator stuff is an implementation detail

  • returns a promise

  async function superSteve (x) {
    const y = await inc()
    return x + y
  }

  async steveception (x) {
    try {
      const a = await superSteve(x)
      const b = await superSteve(a)
      const c = await superSteve(b)

    } catch (err) {
      console.error(err)
      let a = b = c = 1

    } finally {
      return a + b + c
    }
  }
  • usable today with transpilers like babel (stage 1)

  • declaratively say that we are going to wait on inc

  • the generator stuff is an implementation detail

  • returns a promise
  • try/catch/finally

  async function superSteve (x) {
    const y = await inc()
    return x + y
  }

  function pat (x) {
    return inc()
      .then(y => x + y)
      .catch(::console.error)
  }

  function cam (x, cb) {
    inc((err, y) => {
      cb(err, x + y)
    })
  }

  async function superSteve (x) {
    const y = await inc()
    return x + y
  }

  function steve (x) {
    const y = inc()
    return x + y
  }

The Illustrations

– by –

@mrdougal

Async & Await

– by –

@cccc00 – James Hunter

Made with Slides.com