How asynchronous requests are handled

Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

In daily development, we often need to make an asynchronous request, get the result of the asynchronous request, and then go to another interface to find the corresponding value through the result

/ / requirements:
// 1> url: exmaple -> res: example
// 2> url: res + "aaa" -> res: example-aaa
// 3> url: res + "bbb" => res: exmaple-aaa-bbb
Copy the code

Many times the callback

// Use timer to simulate request
function request(url) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url) {
        resolve(url)
      } else {
        reject('url is not defined')}},1000)
  })
}

request('example').then(res= > {
  request(`${res}-aaa`).then(res= > {
    request(`${res}-bbb`).then(res= > {
      console.log(res)
    })
  })
})
Copy the code

This solution is the simplest and most straightforward

But if we need too many asynchronous requests, it’s easy to see the callback hell shown above

This is very detrimental to maintenance and extension later

The return value for then in the Promise

// Use timer to simulate request
function request(url) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url) {
        resolve(url)
      } else {
        reject('url is not defined')}},1000)})}// This workaround is to change the nesting of callback functions to same-level calls
// But it still does not fundamentally solve the maintenance and extension problems caused by multiple callbacks
request('example').then(res= > request(`${res}-aaa`))
.then(res= > request(`${res}-bbb`))
.then(res= > console.log(res))
Copy the code

Promise + generator

The solution is to write asynchronous code in a synchronous manner

This is the best solution until async + await comes along

// Use timer to simulate request
function request(url) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url) {
        resolve(url)
      } else {
        reject('url is not defined')}},1000)})}function* fetchData() {
  let res = yield request('example')
  res = yield request(`${res}-aaa`)
  res = yield request(`${res}-bbb`)
  console.log(res)
}

// Execute the generator function
function execGenerator(generatorFn) {
  const generator = generatorFn()

  function exec(result) {
    const { done, value: promise } = generator.next(result)

    if (done) {
      return result
    }

    promise.then(res= > {
      exec(res)
    })
  }

  exec()
}

execGenerator(fetchData)
Copy the code

In practice, there is a third-party library, CO, that can actively help us execute generator functions without having to write the corresponding execGenerator manually

#The installation
$ npm i co
Copy the code
// Use timer to simulate request
function request(url) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url) {
        resolve(url)
      } else {
        reject('url is not defined')}},1000)})}function* fetchData() {
  let res = yield request('example')
  res = yield request(`${res}-aaa`)
  res = yield request(`${res}-bbb`)
  console.log(res)
}

const co = require('co')

co(fetchData)
Copy the code

async + await

Async +await is the keyword introduced in ES8. It is the actual way to handle requests asynchronously in the current development

It can be assumed that async+await will still be converted to Promise+Generator when parsing

Async +await is just a syntactic sugar form of Promise+Generator

function request(url) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url) {
        resolve(url)
      } else {
        reject('url is not defined')}},1000)})}async function fetchData() {
  let res = await request('example')
  res = await request(`${res}-aaa`)
  res = await request(`${res}-bbb`)
  console.log(res)
}


fetchData()
Copy the code

Basic definition of async functions

The async keyword is used to declare an asynchronous function

async function foo() {}

const bar = async() = > {}class Person {
  async baz(){}}Copy the code

Asynchronous functions execute the process

Asynchronous functions perform the same internal code execution as normal functions and are executed synchronously by default

async function foo() {
  console.log('Script in foo')}console.log('script started')
foo()
console.log('script endedd')

/* => script started foo script endedd */
Copy the code

The difference between an asynchronous function and a normal function

Difference 1: Asynchronous functions can also return values, but the return value of asynchronous functions is wrapped in promise.resolve

// do not write the declaration even in asynchronous functions
// The default return value is also a promise, but promise. Resolve (undefined)
async function foo() {}const promise = foo()
promise.then(res= > console.log('Asynchronous function return value:', res))
Copy the code
async function foo() {
  return 123
}

const promise = foo()
promise.then(res= > console.log(res)) / / = > 123
Copy the code
async function foo() {
  // If the asynchronous function returns a thenable object
  // Then the returned Thenable object is automatically executed to get the final value
  return {
    then(resolve, reject) {
      resolve(123)}}}const promise = foo()
promise.then(res= > console.log(res)) / / = > 123
Copy the code
async function foo() {
  return new Promise(resolve= > resolve(123))}const promise = foo()
promise.then(res= > console.log(res)) / / = > 123
Copy the code

When an asynchronous function throws an exception, it does not report an error like a normal function, but passes it as a Reject, a Promise

async function foo() {
  throw new Error('something error in async function')}const promise = foo()
promise.catch(e= > console.log(e))
Copy the code

Difference 3: You can use the await keyword inside asynchronous functions, but not inside normal functions

function request() {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve(123)},1000)})}async function foo() {
  // await is followed by an expression that returns a Promise
  // When the state of promise returned behind await changes to Resolved.
  // The result is assigned to res as the value of the return value of the await expression
  const res = await request()

  // The logic behind an await expression waits for the asynchronous code corresponding to an await expression to complete execution
  // We can think of the logic behind the await expression as the logic that needs to be written in the then method of the promise returned by the await expression
  console.log(res)
}

foo()
Copy the code
// If await is followed by an ordinary value, the value will be returned directly
function request() {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve(123)},1000)})}async function foo() {
  // If "await" is followed by a basic datatype
  Resolve (123)
  const res = await 123

  console.log(res) / / = > 123
}

foo()
Copy the code

If the expression following await returns a Promise that is a resolve state, the reject result is used directly as the resolve value of the function’s Promise.

function request() {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve(123)},1000)})}async function foo() {
  const res = await {
    then(resolve) {
      resolve(123)}}console.log(res) / / = > 123
}

foo()
Copy the code
function request() {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve(123)},1000)})}async function foo() {
  const res = await new Promise(resolve= >  resolve(123))

  console.log(res) / / = > 123
}

foo()
Copy the code

If the expression following await returns a reject Promise, the reject result will be used as the reject value of the function’s Promise.

function request() {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      reject(123)},1000)})}async function foo() {
  try {
    const res = await request()
    console.log(res)
  } catch(err) {
    console.log('err', err)
  }
}

foo()
Copy the code