Async JavaScript

JavaScript is a single-threaded language which means it has a single call stack. This means that JavaScript in the browser can only do one thing at at time.

As a result, if we have a stack of operations to perform and one of them is really slow, everything else has to wait until the slow operation completes. This is called blocking

So when we get a network request, we have two options:

 

1. Do the network request right away, and block everything else.

 

2. Queue the network request, and run it when we have some spare time.

This used to be done with callbacks

fetchFromSomeApi(function(data) {
  // now we have the data we need
})

but say you had to make 3 requests:

fetchFromSomeApi(function(data1) {
  fetchFromSomeOtherApi(function(data2) {
    fetchFromSomeOtherOtherApi(function(data3) {
      // well this is confusing.
      // and what if one of these goes wrong?
    })
  })
})

So we moved on, and in ES2015, we gained Promises.

Promises can resolve or reject.

 

A resolved promise can be said to have fulfilled.

 

A rejected promise is said to have been rejected.

 

A promise can also be pending, where we don't know the state. For example, we made a network request but haven't had a response back.

 

Promise.resolve can create a promise that resolves with a value.

Promise chains.

These can be thought of like a pipeline.

this is how we give a function that will be called with the value inside the box.

promise functions can return the next value

Promise.resolve(5)
    .then(value => {
        return value + 1;
    }).then(value => {
        console.log(value)
    })

Let's play with promises.

 npm run exercise async 1

open the console!

Wrapping callbacks with promises

setTimeout(() => {
  console.log('I will run after 5 seconds!')
}, 5000)

if a function uses callbacks, we can wrap it in a promise

new Promise()

const timeoutPromise = new Promise(function(resolve) {
  setTimeout(() => resolve(), 5000)
})

timeoutPromise.then(() => {
  console.log('I will run after 5 seconds')
})

when we create a new promise, we get a function that we can call when we want the promise to resolve

const timeoutPromise = new Promise(function(resolve) {
  setTimeout(() => resolve(), 5000)
})

timeoutPromise.then(() => {
  console.log('I will run after 5 seconds')
})
 npm run exercise async 2

when promises go wrong

Promise.resolve(5).then(value => {
  throw new Error('Jack made this promise go wrong on purpose')
})

"uncaught"

Promise.resolve(5).then(value => {
  throw new Error('Jack made this promise go wrong on purpose')
}).catch(e => {
  console.log('We caught the error!')
})
 npm run exercise async 3

error handling

.then/.catch in promise chains

we catch the error and deal with it

and return a new value

does this get run?

promise chains with error handling

 npm run exercise async 4

promises returning promises

you can never have nested promises.

 npm run exercise async 5

the fetch API

fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => {
    return response.json()
  })
  .then(todo => {
    console.log('got todo')
  })
fetch('https://jsonplaceholder.typicode.com/todos/1')
  .then(response => {
    return response.json()
  })
  .then(todo => {
    console.log('got todo')
  })

returns a promise that resolves with the response, parsed to JSON.

wrapping the fetch API promise in a function

 npm run exercise async 6

sequential promises

fetch a photo, and then fetch its album

 npm run exercise async 7

parallel requests

fetch recent photos and recent albums

Promise.all

 npm run exercise async 8
Promise.all([
  Promise.resolve(5),
  Promise.resolve(6)
]).then(values => {
  console.log(values) // [5, 6]
})

async / await

Part of ES2017

async / await is syntactic sugar over promises

when you write async/await, you are using promises

fetch('/posts').then(response => response.json()).then(posts => {...})

const posts = await fetch('/posts').then(response => response.json())

you can only use await in a function that's marked with async

the async keyword denotes a function that returns a promise

const fetchPhoto = async id => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/photos/${id}`
  )
  return response.json()
}

we don't need await here because the return value is automatically wrapped in a promise, because our function is async

 npm run exercise async 9
const fetchPhoto = async id => {
  const response = await fetch(
    `https://jsonplaceholder.typicode.com/photos/${id}`
  )
  return response.json()
}
Promise.resolve(5)
  .then(value => value + 1)
  .then(value => value + 2)
  .then(value => value + 3)
  .then(value => {
    logPromiseValue(1, value)
  })

const asyncVersion = async () => {
  const firstValue = await Promise.resolve(5)
  logPromiseValue(2, firstValue + 1 + 2 + 3)
}

async/await can simplify promise chains

simplify the promise chain using async/await

 npm run exercise async 10

straight into another one! fetch a photo and its album using async await

 npm run exercise async 11

error handling with async await

we simply use try/catch

try {
  const response = await fetch('/photos')
} catch (e) {
  // got an error!
}
try {
  const response = await fetch('/photos')
  const nextThing1 = await fetch(...)
  const nextThing2 = await fetch(...)
  const nextThing3 = await fetch(...)
  const nextThing4 = await fetch(...)

} catch (e) {
  // this will catch all errors from above.
}

catch the error!

 npm run exercise async 12

async/await is sequential!

const photos = await fetchAllPhotos()




const albums = await fetchAllAlbums()

these are not run in parallel!

 

we will wait for photos before fetching albums

because async/await uses promises, we can use async with promises

const photosAndAlbums = await Promise.all([
  fetchPhotos(),
  fetchAlbums()
])

this will run in parallel as we expect

fix up the slowness

 npm run exercise async 13

a...sync we are done here!

setting up with Babel

 

https://stackoverflow.com/a/28709165

TLDR:

babel-plugin-transform-runtime

Async JS

By Jack Franklin