This is the third day of my participation in the August More text Challenge. For details, see: August More Text Challenge

Promise

Basic usage

Promise’s simple encapsulation and use

/ / packaging
functionRoll dice () {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve(Math.floor(Math.random() * 6) + 1)},3000)})}/ / useThen (Success1, Failed1).then(Success2, Failed2)Copy the code

Ma Mi task model

  • Ma is MacroTask, Mi is MicroTask
  • Ma is followed by Mi, that is, macro tasks are executed first and then microtasks are executed
  • When JavaScript runs, in addition to a main thread running, the engine also provides a task queue containing the various asynchronous tasks that need to be processed by the current program
  • In fact, JS originally only had one task queue. In order to make Promise callback execute earlier, an asynchronous task queue was inserted to hold Mi tasks
  • Macro tasks: setTimeout(), setInterval(), setImmediate(), I/O, UI rendering (the usual timers, user interaction events, and so on)
  • Microtasks: Promise, process. NextTick, Object. Observe, MutationObserver

Promise’s other apis

Promise.resolve(result): Make a success (or failure)

Manufacturing success

functionRoll dice () {
  return Promise.resolve(4)}/ / equivalent to the
functionRoll dice () {
  return new Promise((resolve, reject) = > {
    resolve(4)})} ().then()n= > console.log(n)) / / 4
Copy the code

Manufacturing failed

functionRoll dice () {
  // Here promise. resolve accepts a failed Promise instance (reject).
  return Promise.resolve(new Promise((resolve, reject) = >Reject ()))} reject().then()n= > console.log(n)) // 1 Uncaught (in promise) undefined
Copy the code

The issue of accepting the promise. resolve parameter is quite clear in the ECMAScript 6 introduction

If the argument is a Promise instance, promise. resolve returns the instance unchanged. If the parameter is a raw value, or if there is no parameter, promise. resolve will simply return a Promise object in the resolved state.

Promise.reject(reason): Create a failure

Promise.reject('I was wrong')
/ / equivalent to the
new Promise((resolve, reject) = > reject('I was wrong'))

Promise.reject('I was wrong').then(null.reason= > console.log(reason)) / / I am wrong
Copy the code

Promise. All (array): Wait for all to succeed, or one to fail

All successful, return an array of all successful Promise results

Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)])
  .then(values= > console.log(values)) / / [1, 2, 3]
Copy the code

As soon as one fails, it ends, returning the value of the first rejected failure state

Promise.all([Promise.reject(1), Promise.resolve(2), Promise.resolve(3)])
  .then(values= > console.log(values)) // Uncaught (in promise) 1
Copy the code

Promse.all is often useful when multiple asynchrons need to be processed;

However, in some special cases, using promse. all may not be convenient

For example, if we have three requests, request1, request2, and Request3, we need to process all three requests uniformly, and we need to get all the responses whether the request succeeds or fails. If you use promise. all([request1, request2, request3]), request1 will fail, and request2 and request3 will not be executed.

How to solve the problem that promise.all () interrupts after the first Promise fails?

Use.then() to return a Promise in the resolved state

// Three requests
const request1 = () = > new Promise((resolve, reject) = > {
 setTimeout(() = > {
   reject('The first request failed')},1000)})const request2 = () = > new Promise((resolve, reject) = > {
 setTimeout(() = > {
   reject('Second request failed')},2000)})const request3 = () = > new Promise((resolve, reject) = > {
 setTimeout(() = > {
   resolve('Third request successful')},3000)})Promise.all([
  request1().then(value= > ({ status: 'ok', value }), reason= > ({ status: 'not ok', reason })),
  request2().then(value= > ({ status: 'ok', value }), reason= > ({ status: 'not ok', reason })),
  request3().then(value= > ({ status: 'ok', value }), reason= > ({ status: 'not ok', reason }))
]).then(result= > console.log(result))
Copy the code

You can encapsulate the.then operation on each request

const x = promiseList= > promiseList.map(promise= > promise.then(value= > ({
  status: 'ok',
  value
}), reason= > ({
  status: 'not ok',
  reason
})))

const xxx = promiseList= > Promise.all(x(promiseList))

xxx([request1(), request2(), request3()])
  .then(result= > console.log(result))
Copy the code

The print result is as follows:

Promise. AllSettled (array): Waits for all states to change

Promise.allSettled([Promise.reject(1), Promise.resolve(2), Promise.resolve(3)])
  .then(result= > console.log(result))
Copy the code

The print result is as follows:

We can see that the function of Promise.allSettled is actually the same as the XXX function we implemented above, so we can use promise. allSettled directly for the scene mentioned above, which is more concise.

Promise. Race (array): Wait for the first state to change

Promise.race([request1(), request2(), request3()]).then((result) = > {
  console.log(result)
}).catch((error) = > {
  console.log(error) // The first request failed
})
Copy the code

Promise.race([request1, request2, request3]) returns the result of which request was responded to first, regardless of whether the result itself was in a success or failure state (in this case, request1 was the first response).

The PROMISe. race API is not normally used, but it is useful in some scenarios. For example, if you have the same server code deployed on multiple servers and want to get data from a product list interface, you can write the address of the product list interface on all the servers in race. The server that responds quickly will get data from the server first.

Application scenarios of Promise

Process a result multiple times

Roll dice (). Then (v= > v1).then(v1= > v2)
Copy the code

After the first callback completes, it passes the result as an argument to the second callback.

serial

  • Here’s the paradox: Once the promise appears, the task has already been performed
  • So it’s not promise serial, it’s task serial
  • Solution: put tasks into a queue, complete one and then proceed to the next (Reduce to implement Promise serial execution)

parallel

All, promise.allsettled, and promise.race can all be thought of as processing tasks in parallel

Here you might wonder, isn’t JS single threaded, how can we execute tasks in parallel?

This refers to the task of doing the network request in parallel, which is actually done by the browser, not by JS, just like setTimeout is a function of the browser, not JS, and setTimeout is just an interface provided by the browser to JS.

Error handling for Promise

Error handling by itself

The error handling of the PROMISE itself is actually quite useful. It can be done directly in the second callback parameter of the.then

promise.then(s1, f1)
Copy the code

Or use the.catch syntax sugar

// The above grammar sugar
promise.then(s1).catch(f1)
Copy the code

It is recommended to always use the catch() method rather than the second argument of the THEN () method, because the second method can catch errors in the previous THEN method execution and is closer to synchronous writing (try/catch).

Global error handling

Take axios for example, the Axios cheat sheet

After error handling

  • If you don’t keep throwing mistakes, then the mistakes don’t happen anymore
  • If you continue to throw errors, then subsequent callbacks will continue to handle the errors

The front end seemed unhappy with Promise

There are 6 reasons why Async/Await can replace promises, mainly in the following 6 aspects:

  • concise
  • Error handling
  • Conditional statements
  • The median
  • The error stack
  • Debugging (in.thenSet a breakpoint in the code block, use the Step Over shortcut, and the debugger will not skip to the next one.thenBecause it only skips the asynchronous code)

async / await

Async/await basic usage

The most common usage

const fn = async() = > {const temp = await makePromise()
  return temp + 1
}
Copy the code

Pros: No indentation at all, just like writing synchronous code

Encapsulates an async function

Encapsulation and use of async

functionRoll dice () {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve(Math.floor(Math.random() * 6) + 1)},3000)})}async function fn() {
  const result = awaitRoll dice ()console.log(result)
}

fn()
Copy the code

Using a try… Catch Performs error handling

async functionRoll dice () {
  throw new Error('The dice is broken')}async function fn() {
  try {
    const result = awaitRoll dice ()console.log(result)
  } catch (error) {
    console.log(error)
  }
}

fn()
Copy the code

Why async is needed

Adding async in front of the function seems redundant. The function where the await is is async, isn’t it?

One reason:

Before the ES standard async/await came out, some people implemented await themselves with functions, in order to be compatible with await(XXX) of normal functions in old code (to distinguish await in old code from async/await in the new ES standard). And async itself doesn’t really mean anything.

You might say that async functions implicitly return a Promise object, but that’s not a reason to have async in front of a function.

  • Why must js methods that use await be defined as async?
  • What does the async keyword do in C#?

Await error handling

It is common practice to use try/catch to handle both synchronous and asynchronous errors

let response
try {
  response = await axios.get('/xxx')}catch (e) {
  if (e.response) {
    console.log(e.response.status)
    throw e
  }
}
console.log(response)
Copy the code

But there’s a better way to write it, like this

const errorHandler = error= > {
  console.log(error)
  // Note that an error is thrown
  throw error
}

// Only one line of code can handle success and failure
const response = await axios.get('/xxx').then(null, errorHandler)

// Or use the catch syntax for sugar
const response = await axios.get('/xxx').catch(errorHandler)
Copy the code

The errorHandler function does not return a value directly. It must throw an error. In the case of a failed call request, the return value in errorHandler is directly assigned to the response (colloquial “Promise eats the error”). Throwing an error in the errorHandler ensures that the response will come only if the request succeeds, and it will go into the errorHandler function if the request fails

Here’s a practical example

const ajax = function() {
  return new Promise((resolve, reject) = > {
    reject('Here's a reminder of failure.')
    // resolve(' this is the result of success ')})}const error = (error) = > {
  console.log('error:', error)
  return Promise.reject(error)
}

async function fn() {
  const response = await ajax().then(null, error)
  console.log('response:', response)
}

fn()
Copy the code

As you can see, we can handle both Promise success and failure cases with just one line of code, and the vast majority of Ajax calls can be handled this way.

Therefore, with async/await, it is not necessary to use try/catch for error handling.

I used to fall into the trap of thinking that ‘await’ and ‘.then ‘are the opposite of’ await ‘and I always thought that’ await ‘should not be used again.

But no, async/await is just syntax sugar for.then. As in the above example,.then and await are perfectly usable together, with errors handled in.then and only successful results accepted to the left of await.

In addition, we can use interceptors to handle common errors such as 4xx/5xx globally, and errorHandler can also be placed in the interceptor.

Contagion of await

Code:

console.log(1)
await console.log(2)
console.log(3) // await makes the code asynchronous. If you want it to execute immediately, just place it in front of the await
Copy the code

Analysis:

  • awaitWill make all of itThe code on the left and the code belowBecome asynchronous code
  • console.log(3)It becomes an asynchronous task
  • Promise is also contagious (synchronous mutation step), put.thenThe code in the callback function becomes asynchronous, but compared toawait..thenThe following code does not become asynchronous
  • Callbacks are not contagious

Application scenarios for await

Process a result multiple times

const r1 = await makePromise()
const r2 = handleR1(r1)
const r3 = handleR2(r2)
Copy the code

serial

Native serial (when multiple await await are executed side by side, they are executed from top to bottom, with subsequent await await)

await promise1
await promise2
await promise3
...
Copy the code

parallel

All ([p1, p2, p3]), await promise. allSettled([p1, p2, p3]), and await promise. race([p1, p2, p3]) are parallel

There is a bug in looping

Normally, await should be executed sequentially even within a loop.

For example, await in a for loop is serial (after and before)

async function runPromiseByQueue(myPromises) {
  for (let i = 0; i < myPromises.length; i++) {
    awaitmyPromises[i](); }}const createPromise = (time, id) = > () = >
  new Promise((resolve) = >
    setTimeout(() = > {
      console.log("promise", id);
      resolve();
    }, time)
  );
runPromiseByQueue([
  createPromise(3000.4),
  createPromise(2000.2),
  createPromise(1000.1)]);/ / 4 2 1
Copy the code

But in some loops, such as forEach and map, await is executed in parallel (not preceded by await)

async function runPromiseByQueue(myPromises) {
  myPromises.forEach(async (task) => {
    await task();
  });
}

const createPromise = (time, id) = > () = >
  new Promise((resolve) = >
    setTimeout(() = > {
      console.log("promise", id);
      resolve();
    }, time)
  );
runPromiseByQueue([
  createPromise(3000.4),
  createPromise(2000.2),
  createPromise(1000.1)]);4/1/2
Copy the code

For await… Of to fix the bug

async function runPromiseByQueue(myPromises) {
  // Asynchronous iteration
  for await (let item of myPromises) {
    console.log('promise', item); }}const createPromise = (time, id) = >
  new Promise((resolve) = >
    setTimeout(() = > {
      resolve(id);
    }, time)
  );

runPromiseByQueue([
  createPromise(3000.4),
  createPromise(2000.2),
  createPromise(1000.1)]);/ / 4 2 1
Copy the code

Reference

  • Overview of Asynchronous Operations
  • Event loops: microtasks and macro tasks
  • Event Loop, timer, nextTick
  • Understand and use promise. all and promise. race
  • For await of