preface

In JavaScript, code is executed in a single thread, and for this reason, all network operations and browser events in JavaScript must be executed asynchronously. So various asynchronous schemes began to appear and gradually rationalized, simple words!

Asynchronous processing

Common asynchronous processing schemes used in development include Callback, Promise, Generator and async/await. Here are the main similarities and differences between these schemes:

Callback function

Suppose we define a getData function for a data request:

function getData(url, callback) {
  // Simulate a data request
  setTimeout((a)= > {
    let res = {
      url: url,
      data: {}
    }
    callback(res)
  }, 1000)}Copy the code

The requirement now is that we need to request the server three times in turn, and the data in each request must be executed on the basis of the last success:

getData('/api/page/1? params=123',(res1) => {
  console.log(res1);
  getData(`/api/page/2? params=${res1.data.params}`, (res2) => {
    console.log(res2);
    getData(`/api/page/3? params=${res2.data.params}`, (res3) => {
      console.log(res3); })})})Copy the code

At 🌰 above, we can see the first url:/ API /page/1? Params =123, second time url: / API /page/2? Params =${res1.data.params}, depending on the first request data, the third url: / API /page/2? Params =${res2.data.params}, depending on the data of the second request. Since each data request depends on the last one, we will write the next data request inside the function in the form of a callback function, which is what we call back to hell!

Promise

Let’s use Promise to fulfill the same requirement:

First we need to rewrite our getData function as a Promise

  function getDataPromise(url) {
    return new Promise((resolve, reject) = > {
      setTimeout((a)= > {
        let res = {
          url: url,
          data: {}
        }
        resolve(res)
      }, 1000)})}Copy the code

The logical code should become:

  getDataPromise('/api/page/1? params=123')
    .then(res1= > {
      console.log(res1);
      return getDataPromise(`/api/page/2? params=${res1.data.params}`)
    })
    .then(res2= > {
      console.log(res2);
      return getDataPromise(`/api/page/3? params=${res2.data.params}`)
    })
    .then(res3= > {
      console.log(res3);
    })
Copy the code

After we write this, we find that we return a Promise object each time the data request is successful (then), so that we can use it next time. This way, we avoid going back to hell, but it’s not perfect. As our requests get more complex, we find that our code gets more complex.

To avoid this async/await is created.

async/await

The getData function remains the same, Promise

  function getDataPromise(url) {
    return new Promise((resolve, reject) = > {
      setTimeout((a)= > {
        let res = {
          url: url,
          data: {}
        }
        resolve(res)
      }, 1000)})}Copy the code

The required code becomes:

  async function getData () {
    let res1 = await getDataPromise('/api/page/1? params=123');
    console.log(res1);
    let res2 = await getDataPromise(`/api/page/2? params=${res1.data.params}`);
    console.log(res2);
    let res3 = await getDataPromise(`/api/page/2? params=${res2.data.params}`);
    console.log(res3);
  }

Copy the code

Async /await is based on promises. Async returns a Promise. In fact async/await can be seen as syntactic sugar for Generator asynchronous processing 👇 let’s see how to implement this code using Generator

Generator

  // Asynchronous functions are still Promise
  function getDataPromise(url) {
    return new Promise((resolve, reject) = > {
      setTimeout((a)= > {
        let res = {
          url: url,
          data: {}
        }
        resolve(res)
      }, 1000)})}function * getData() {
    let res1 = yield getDataPromise('/api/page/1? params=123');
    console.log(res1);
    let res2 = yield getDataPromise(`/api/page/2? params=${res1.data.params}`);
    console.log(res2);
    let res3 = yield getDataPromise(`/api/page/2? params=${res2.data.params}`);
    console.log(res3);
  }

Copy the code

You can actually break it down:

  let fn = getData()
  fn.next().value.then(res1= > {
    fn.next(res1).value.then(res2= > {
      fn.next(res2).value.then( (a)= > {
        fn.next()
      })
    })
  })

Copy the code

As you can see from the above code, the.value method of next() returns a Promise at each step, so we can add then methods, after which I continue to call next() until the function completes. In fact, we don’t need to write the above code manually, we can encapsulate it:

  function run(gen) {
    let fn = gen()

    function next(data) {
      let res = fn.next(data)
      if (res.done) return res.value
      res.value.then((info) = > {
        next(info)
      })
    }
    next()
  }

  run(getData)
Copy the code

The run method is used to automatically perform an operation, which can be regarded as the Generator performing a recursive operation.

This encapsulates asynchronous operations inside functions. It is not hard to see that async/await has many similarities with Generator, but async/await is semantically easier to understand.

We do not need to define run() when using async/await, it is already defined and encapsulated internally, which is why async/await is a syntactic sugar for Generator asynchronous processing.

Promise

We have described the differences between Callback, Promise, Generator and async/await functions. Let’s talk more about promises.

Promise.prototype.then()

  • role

Both the then and promise.prototype.catch () methods return promises, and they can be called chained – an operation called composite composition.

  • parameter

The first parameter: the callback function when the state changes from Pending -> depressing

Second parameter: callback function when the status is from Pending -> Rejected

  • Return value: new Promise instance (note not the original Promise instance)

  • The characteristics of

Since the THEN method returns a new Promise instance, the THEN method can be called chained. The chained then method has two characteristics:

First: The callback function of the later THEN method takes the return value of the previous THEN method

Second: If the previous THEN method returns a Promise instance, the later THEN method’s callback waits for the state of the Promise instance to change before executing

Promise.prototype.catch

  • describe

The catch method can be used for error handling in your Promise combination.

Internally calls Promise.prototype.then on the object upon which is called, passing the parameters undefined and the onRejected handler received; then returns the value of that call (which is a Promise).

Take a look at the following code:

const promise = new Promise(function (resolve, reject) {
    setTimeout((a)= > {
        reject('err')},1000)
})

promise.then(
    res= > console.log('s1'),
    err => console.log('e1')
).then(
    res= > console.log('s2')
).catch(
    err= > console.log('e2'))Copy the code
e1
s2
Copy the code

We can see that we caught an error in the first then error handler, so we printed E1. Then the error has already been caught, so there is no need to catch again, so e2 is not printed, which is normal, but s2 is printed… So to avoid this, the code should be changed to:

promise.then(
    res= > console.log('s1')
).then(
    res= > console.log('s2')
).catch(
    err= > console.log('e2'))Copy the code

So this is just going to output E2

Promise.prototype.finally

We can use finally when we want to take a step forward when a Promise succeeds or fails

promise.then(
    res= > console.log('s1')
).catch(
    err= > console.log('e1')
).finally(
    (a)= > console.log('end'))Copy the code

It is easy to see that.finally is nothing more than a.then of the same successful and failed callback function.

Promise.all(iterable);

  • Iterable An iterable object, such as Array or String;

  • The return value

    • If the argument passed is an empty iterable, return a Promise that is already resolved.
    • If the parameters passed in do not contain any promises, an Asynchronous completion Promise is returned. Note: Google Chrome 58 returns a Promise in this case that the state is already resolved.
    • Otherwise, a pending Promise is returned. This returned promise then becomes asynchronously completed or failed when all promises are completed or when one promise fails. See below for an example of “asynchrony or synchronization for Promise.all”. The return values will be in the order of the promise within the parameters, not determined by the order in which the promise was called.

🌰 :

  const p = Promise.all([promise1, promise2, promise3])

  p.then(
      (res) = > {
          // res is the result array})Copy the code

This will be a big pity only when the states of all Promise instances become fulfilled. Promise.all generates an instance of Rejected, as long as the state of one Promise instance changes to Rejected.

Promise.race

  • Function: Similar to promise.all, it wraps multiple Promise instances into one Promise instance.

  • Parameter: same as promise.all

  • Features:

The state of the Promise instance generated by the promise.race method depends on the state of the Promise instance whose state changes first of all the Promise instances it wraps.

The race function returns a Promise that will be completed in the same way as the first Promise passed. It may be a high degree of completion or rejection, depending on which of the two is the first. If the passed iteration is empty, the returned promise will wait forever. If the iteration contains one or more non-promise values and/or resolved/rejected promises, promise.race resolves to the first value found in the iteration.

  • Example: Request timeout
const promise = Promise.race([
    getData('/path/data'),
    new Promise((resolve, reject) = > {
        setTimeout((a)= > { reject('timeout')},10000)
    })
])

promise.then(res= > console.log(res))
promise.catch(msg= > console.log(msg))
Copy the code

Promise.resolve()

  • Effect: Converts an existing object (or original value) into a Promise object.

  • Parameters: Parameters can be of any type. Different parameters have different behaviors

    • If the argument is onePromiseObject is returned unchanged
    • If the argument is onethenableObject (that is, withthenMethod object), thenPromise.resolveI’m going to convert it toPromiseObject and execute immediatelythenmethods
    • If the argument is a normal object or a primitive valuePromise.resolveIt’s going to be packaged asPromiseObject in the state offulfilled
    • With no arguments, a state offulfilled çš„ Promiseobject

Promise.reject()

  • An overview of the

The promise.reject (reason) method returns a Promise object with the reject reason reason argument.

Using an instance of Error to get the reason for the Error is often helpful for debugging and selective Error catching.

  • Argument: Any argument that will be used as a reason for failure:
Promise.reject('err')

/ / equivalent to the
new Promise(function (resolve, reject) {
    reject('err')})Copy the code

Unified use of Promise

In fact, we can synchronize code in JS or we can use Promise


function a() {
  console.log('aaa')}/ / equivalent to the
const p = new Promise((resolve, rejext) = > {
  resolve(a())
})
Copy the code

That’s All

Or click on Promise