A detailed study of Promise and related knowledge

Learn key phrases:
Promise
promise learning
promise.all
promise.race
promise.resolve

1. Write in front

Promise is something that cannot be avoided by the front end, so we must study it well. The purpose of writing this article is to deepen the learning and use of promise.

2. Start

2.1 Preparation

First create a folder and create a new index.html and index.js in it
We watch the effect of using promise in the browser environment

2.2 Watch

We need to first take a look at what this promise is

let o1 = new Object()
console.log(o1)

let p1 = new Promise((resolve, reject) => {<!-- --> })
console.log(p1)

We can see that promise has two more things than normal objects, one is PromiseState, the value is pending; the other is PromiseResult, the value is undefined

We can see from the instantiation method and printing object that promise is a constructor or class, which seems to just pass in a function during instantiation. So let’s continue to look at a simple promsie example, Let’s see what this promise becomes

let x = 1
let p1 = new Promise((resolve, reject) => {<!-- -->
  if (x < 2) {<!-- -->
    resolve(x)
  } else {<!-- -->
    reject(x)
  }
})
p1.then(value => {<!-- -->
  console.log('value:' + value) // value:1
}, reason => {<!-- -->
  console.log('reason:' + reason)
})
console.log(p1)

We see that PromiseState becomes fulfilled and PromiseResult becomes 1

This doesn’t seem like a big deal. It looks the same at first glance. It seems like it should be like this.

Next, we click on prototype to see what methods are on the prototype

There are not many methods on the prototype, we only need to pay attention to the catch finally and then methods.
ok, so we have completely seen the relevant content of the promise instance. Next, let’s take a look at what is in the promise constructor.
Click on constructor

The methods on the constructor that we need to pay attention to are all allSettled any race reject and resolve methods

Okay, now you have completely read the promise. It is ok if you are familiar with it. Then start learning.

2.3 Basic knowledge

You may have some doubts, so let’s introduce the basic knowledge.

1. PromiseState

A promise has three states, namely pending, fulfilled and rejected

  1. pending is the basic state of a promise. All promises are in the pending state at the beginning.
    Understood as a promise is about to happen
  2. fulfilled A callback function that indicates that this promise has been completed and executed successfully
    Understood as a promise is kept
  3. rejected Indicates that this promise failed and the callback function failed to execute.
    Understood as a promise is broken and rejected

Special Note:

  • A promise can only have one state, that is, a promise will not happen at the same time, keep the promise, and break the promise at the same time.
  • Status only can change from pending to fulfilled or rejected
  • Once the state changes, there will be no second change, that is, the state is solidified.

2. PromiseResult

As the name suggests, it actually refers to the result value of a promise

  • If the promise is fulfilled and the promise is fulfilled, the value will be the parameter passed in the successful callback.
  • If the promise is broken and the contract is rejected, the value will be the parameter passed in the failure callback.
  • Since the state will not change twice, the result value will not change, and it will be the same result no matter how many times it is called.

3. Two parameter functions in promise

Let’s look at the following code

let x = 1;
let p1 = new Promise((resolve, reject) => {<!-- -->
  if (x < 2) {<!-- -->
    resolve(x);
  } else {<!-- -->
    reject('An error occurred');
  }
});

p1 is a promise object we newly instantiated. Just like normal instantiation, we pass a parameter into it during initialization, but this parameter is a function. Here is the function below. Doesn’t it look good like this? a lot of

(resolve, reject) => {<!-- -->
  if (x < 2) {<!-- -->
    resolve(x);
  } else {<!-- -->
    reject('An error occurred');
  }
}

This function has a total of two parameters, resolve and reject, and these two parameters happen to be two functions, but these two functions are not defined by us. The resolve and reject we write here are just two formal parameters. You You can change your name to whatever you want.

The function we passed in will be automatically called in the constructor of the promise, and the real parameter functions resolve and reject will be passed in.

In our function body, when we want to fulfill the promise, we call the resolve method and put the value we want to pass as a parameter; if we want to reject the promise When we call the reject method, we put the reason for rejection as a parameter; whether it is success or failure, the parameters we put in will become the final PromiseResult

At the same time, the PromiseState knowledge we just learned tells us that after we call the resolve method, the status will change from pending to fulfilled, and after we call the reject method, the status will change from pending to rejected.

You can see that in our code above, x = 1, so x < 2 is the result we want, then we Fulfill the promise call the resolve method and pass x into the simultaneous state It becomes fulfilled, otherwise we Reject the promise, call the reject method and pass in our rejection reason, and the status changes to rejected.

In general, these two parameter functions have nothing to do with us. We only need to call them when we achieve our goals.

2.4 Prototype method of promise

Okay, we basically understand it now. Next, let’s talk about the methods on the prototype.

2.4.1 then() method

First of all, we must first clarify what is meant by fulfilling a promise and what is meant by rejecting a promise.

The first step is for me to make a promise. If I fulfill it, then I will do it. If I refuse, then I will do whatever I want.

The second step is to look at my behavior. Did I fulfill it or refuse it? If I fulfill it, you can do it. If I refuse it, you can do it.

In fact, it is a behavior that makes the next behavior based on the previous behavior.

Let’s look at the following code

let x = 1
let p1 = new Promise((resolve, reject) => {<!-- -->
  if (x < 2) {<!-- -->
    resolve(x)
  } else {<!-- -->
    reject('Something went wrong')
  }
})
p1.then(value => {<!-- -->
  console.log('value:' + value)
}, reason => {<!-- -->
  console.log('reason:' + reason)
})

During the use of this code, we can see that the then() method receives two parameters, and the first parameter is the successful callback. When a promise uses the resolve method, the then() method will enter the first A parameter function, while passing in the parameters in the resolve method;
When a promise uses the reject method, the then() method will enter the second parameter function and pass in the parameters in the reject method.

In the above code, we call the resolve method, so x is passed to the first function as a parameter instead of the value parameter, so value: 1 is printed.

Similarly, if you change x in the above code to 10, the reject method will be called. If an error occurs, the three words will be passed to the second function as parameters to replace the reason parameter, and reason: an error will be printed.

This is the then() method. Isn’t it simple and easy to use?

2.4.2 catch() method

We have learned about the simple and easy-to-use then() method, but I still find it uncomfortable to write two functions in one parameter. Don’t ask, it’s just uncomfortable. What should I do?

Of course, let’s take a look at the catch() method. Let’s look at the following code

let x = 10
let p1 = new Promise((resolve, reject) => {<!-- -->
  if (x < 2) {<!-- -->
    resolve(x)
  } else {<!-- -->
    reject('Something went wrong')
  }
})
p1.then(value => {<!-- -->
  console.log('value:' + value)
}).catch(err => {<!-- -->
  console.log('err:' + err)
})

As you can see, I only wrote one parameter function in then(), so it stands to reason that we should not be able to get the result of rejecting the promise, but we added a catch() method later, and the catch() method The parameter in then() is also a function, so the catch() method is specifically used to capture rejected promises. This is much clearer than writing two functions in then(). Let’s take a look at what is printed in the console.

Of course, you may ask, then I can write them all, of course there is no problem, but if I write them all, the catch() method will be invalid.

let x = 10
let p1 = new Promise((resolve, reject) => {<!-- -->
  if (x < 2) {<!-- -->
    resolve(x)
  } else {<!-- -->
    reject('Something went wrong')
  }
})
p1.then(value => {<!-- -->
  console.log('value:' + value)
}, reason => {<!-- -->
  console.log('reason:' + reason)
}).catch(err => {<!-- -->
  console.log('err:' + err)
})

Therefore, it can be considered that the catch() method is the syntactic sugar of the then() method. When the real owner comes out, the substitute will naturally end.

Note
You may have noticed that in the above code, we call the catch() method directly using dot syntax at the end of the then() method, so we realize that each method will return a new promise, so that we can achieve continuous The effect of chain calls

2.4.3 finally() method

As the name suggests, this finally() method is the last method to be executed, and it is a method that will be executed no matter what. It does not receive parameters.

let x = 10
let p1 = new Promise((resolve, reject) => {<!-- -->
  if (x < 2) {<!-- -->
    resolve(x)
  } else {<!-- -->
    reject('Something went wrong')
  }
})
p1.then(value => {<!-- -->
  console.log('value:' + value)
}).catch(err => {<!-- -->
  console.log('err:' + err)
}).finally(a => {<!-- -->
  console.log('finally:', a)
})

We see that a printed by finally is undefined, so the finally() method has no parameters and will definitely be executed.

Note
The finally() method will also return a new promise, so it can also be called in a chain

p1.then(value => {<!-- -->
 console.log('value:' + value)
}).finally(a => {<!-- -->
 console.log('finally:', a)
}).catch(err => {<!-- -->
  console.log('err:' + err)
})

2.5 Promise’s static methods

The prototype methods that need to be learned before are those three. Next, we will learn about static methods, which are methods declared in the class using static.

2.5.1 Promise.resolve() method

As for static methods, I think it is easiest to start with the Promise.resolve() method, because its function is the simplest.

Effect: wrap parameters into promise objects

Of course, the parameters here need to be handled on a case-by-case basis

  1. The parameter is a promise object

    If the parameter itself is already a promise object, then return the promise object unchanged.

    let p1 = new Promise((resolve, reject) => {<!-- --> });
    let p2 = Promise.resolve(p1);
    console.log(p1 === p2); // true
    
  2. Parameters are ordinary data

    When the parameter is a number, string, Boolean, undefined, null, array, object, Set, or Map, a new Promise object is returned, and the PromiseState status is fulfilled, and the PromiseResult value is the parameter value.

    console.log(Promise.resolve(123))
    console.log(Promise.resolve('shaoyahu'))
    console.log(Promise.resolve(false))
    console.log(Promise.resolve(undefined))
    console.log(Promise.resolve(null))
    console.log(Promise.resolve([1, 3]))
    console.log(Promise.resolve({<!-- --> name: 'shaoyahu' }))
    console.log(Promise.resolve(new Set([1, 2])))
    console.log(Promise.resolve(new Map([[1, 1], [2, 2]])))
    

  3. no parameters

    Without parameters, a promise with an empty value is returned directly.

    let p1 = Promise.resolve()
    let p2 = Promise.resolve(undefined)
    console.log(p1)
    console.log(p2)
    console.log(p1 === p2)
    

2.5.2 Promise.reject() method

The usage is the same as the Promise.resolve() method, except that the status of the return value is rejected, and there is no other difference.

2.5.3 Promise.all() method

Effect: multiple original promises are packaged into a new promise, and this new promise only has the status of all promises changed to fulfilled or any promise status changed to rejected< /strong> will change from the pending state to the corresponding state

The parameter received by the all() method is an array, and all promise objects are placed in the array. If there are parameters that are not promise objects, the Promise.resolve() method is used by default for wrapping.

To put it simply, all promises succeed if they succeed, and if one fails, they fail. Let’s look at the following code.

let p1 = new Promise((resolve, reject) => {<!-- -->
  resolve('p1')
})
let p2 = new Promise((resolve, reject) => {<!-- -->
  setTimeout(() => {<!-- -->
    resolve('p2')
  }, 2000);
})
let p3 = new Promise((resolve, reject) => {<!-- -->
  resolve('p3')
})
let p = Promise.all([p1, p2, p3, 123])
console.log(p)
p.then(value => {<!-- -->
  console.log(value)
  console.log(p)
})

Let’s take a look at the results. Obviously, p is in the pending state at the beginning. It will change to the fulfilled state after all the promises in the array have returned successfully, and the return value is an array composed of the return values of each promise.

If a promise fails, the returned status will be p in the rejected state, and the return value will be the failure reason of the failed promise.

2.5.4 Promise.race() method

This method is actually similar to the all() method, except that the name is race, racing hehe

Effect: wrap multiple promise instances into a new promise instance. Once a promise in the iterator succeeds or fails, the new promise will succeed or fail immediately.

To put it simply, it only takes the fastest returned value, whether it is success or failure. Let’s look at the following code.

let p1 = new Promise((resolve, reject) => {<!-- -->
  setTimeout(() => {<!-- -->
    resolve('p1')
  }, 1500);
})
let p2 = new Promise((resolve, reject) => {<!-- -->
  setTimeout(() => {<!-- -->
    resolve('p2')
  }, 2000);
})
let p3 = new Promise((resolve, reject) => {<!-- -->
  setTimeout(() => {<!-- -->
    resolve('p3')
  }, 1000);
})
let p = Promise.race([p1, p2, p3])
console.log(p)
p.then(value => {<!-- -->
  console.log(value)
  console.log(p)
})

We can see from the code that the fastest one should be p3, so the result must be p3

2.5.5 Promise.any() method

Effect: Wrap multiple original promises into a new promise. Once one of the original promises succeeds, the successful promise value will be returned, and the new promise status will be changed to fulfilled.

To put it simply, it is a blind version of the Promise.all() method. It does not need to look at the failures, but only the successes. It is an eccentric version of the Promise.race() method. I only take the first one, but I only look at the successes. Let’s look at the following code

let p1 = new Promise((resolve, reject) => {<!-- -->
  setTimeout(() => {<!-- -->
    resolve('p1')
  }, 1500);
})
let p2 = new Promise((resolve, reject) => {<!-- -->
  setTimeout(() => {<!-- -->
    resolve('p2')
  }, 2000);
})
let p3 = new Promise((resolve, reject) => {<!-- -->
  setTimeout(() => {<!-- -->
    reject('p3')
  }, 1000);
})
let p = Promise.any([p1, p2, p3])
console.log(p)
p.then(value => {<!-- -->
  console.log(value)
  console.log(p)
})

p3 is the fastest, but it fails, so the return value is the fastest and successful p1

2.5.6 Promise.allSettled() method

Effect: multiple original promises are packaged into a new promise. When all promise states change, the new promise state is changed to fulfilled, and the return value is processed from an array composed of the original promise return values. value

To put it simply, the Promise.all() method ignores failures and returns all results. Let’s look at the following code

let p1 = new Promise((resolve, reject) => {<!-- -->
  setTimeout(() => {<!-- -->
    resolve('p1')
  }, 1500);
})
let p2 = new Promise((resolve, reject) => {<!-- -->
  setTimeout(() => {<!-- -->
    resolve('p2')
  }, 2000);
})
let p3 = new Promise((resolve, reject) => {<!-- -->
  setTimeout(() => {<!-- -->
    reject('p3')
  }, 1000);
})
let p = Promise.allSettled([p1, p2, p3])
console.log(p)
p.then(value => {<!-- -->
  console.log(value)
  console.log(p)
})

Let’s look at the returned data directly. You’ll understand it right away. It’s very clear.

3. Summary

Promise is an important knowledge point that the front-end must learn. I think everyone knows the above content. I am really good at it. Then, let’s try to rewrite a promise class.

4. End

After learning this, I can finally breathe a sigh of relief. How about it? Have you completely mastered it?
In fact, it is quite difficult, and the difficulty may be more convoluted in actual use, but these are the methods, unless you are just calling the interface

If you encounter any problems that cannot be achieved while following this, please tell me in the comment area or private message, I will mobilize netizens to answer your questions

syntaxbug.com © 2021 All Rights Reserved.