Cause of occurrence

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

Before Promise came along

function foo(url, successCallback, failCallback) {
  // Use timer to simulate network request
  setTimeout(() = > {
    if (url = 'example') {
      successCallback(213)}else {
      failCallback('err')}},2000)
}

foo('example'.msg= > console.log(`success --- ${msg}`),
err= > console.log(`fail --- ${err}`))Copy the code

The way we process asynchronous results is by using callback functions, but how asynchronous functions receive callback functions and how callback arguments are used is defined by the wrappers themselves, which means that different wrappers have different coding styles and encapsulation methods

For callers, if they need to use the method of wrapped asynchronous operation, they have to see how the wrapper is wrapped, that is, to see the document, to see how the API is called, which increases the communication cost between the user and the wrapper. Right

Promises/A+ : Promises/A+ : Promises/A+ : Promises/A+

In ES6, however, there is a unified, official way of encapsulating asynchronous functions, and this is the Promise

The biggest benefit of promises is that they allow asynchronous methods to return values just as synchronous methods do, but only with a promise, a pending state that must be switched to a success or failure state in the future

The basic use

  • Promise is a class that can be translated as a Promise, a Promise, or an appointment
  • A promise is a promise, or a credential, that will later tell the user whether the asynchronous operation was successful and return the corresponding result
Promsie takes one argument, which is a callback function called the executor function
// The executor function is executed immediately when a Promise instance is created
new Promise(() = > console.log('Executor will be executed immediately')) // => Executor will be executed immediately
Copy the code
function getResponse(url) {
  // Store asynchronous functions that need to be executed in executor functions, which can also contain synchronous code
  // Executor needs to pass in two arguments whose types are required to be functions
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url === 'example') {
        // When successful, execute the resolve callback
        resolve('success')}else {
        When it fails, the reject callback is executed
        reject('fail')}},2000)})}const promise = getResponse('example')

// Promise has then methods
// Parameter 1 corresponds to the resolve parameter that will be executed when the resolved state is resolved
// Parameter 2 corresponds to executor reject in rejected === Parameter 2
promise.then(res= > console.log(res), err= > console.log(err))
Copy the code

Three states

  • Pending — Pending
    • The status of the executor function after it has been executed
    • It is not known whether the asynchronous request will ultimately succeed or fail
  • This is a pity (resolved) — Asynchronous success
  • Rejeced – Asynchronous failure

Promise states are locked once they change and cannot be changed

Pending can convert resolved | rejected

But reolved and Rejected are not interchangeable

function getResponse(url) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url === 'example') {
        resolve('success')
        // The code will not report an error
        // But reject is not executed -- silent failure
        reject('fail')}else {
        reject('fail')}},2000)})}const promise = getResponse('example')
promise.then(res= > console.log(res), err= > console.log(err))
Copy the code

resolve

The parameters are either basic data types or plain objects - promise's state is Reolved

function getResponse(url) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url === 'example') {
        resolve('success')}else {
        reject('fail')}},2000)})}const promise = getResponse('example')

promise.then(res= > console.log(res), err= > console.log(err)) // => success
Copy the code

The parameter type is Promise

The state of the promise is handed over, that is, the promise and why does the state end up being determined by the incoming promise

function getResponse(url) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url === 'example') {
        // The resolve state is successful
        // But the argument is a new promise, so the state decision has been handed over
        // This is determined by the parameter Promise
        // So the result is rejected
        resolve(new Promise((resolve, reject) = > reject('Failed')))}else {
        reject('fail')}},2000)})}const promise = getResponse('example')
promise.then(res= > console.log(res), err= > console.log(err)) // => Failed
Copy the code

The argument is a thenable object

When a method implements the THEN method correctly, it is called a thenable object, which is essentially an object that implements the THEN method

The final state of the promise is determined by the then method

function getResponse(url) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url === 'example') {
        resolve({
          // Then methods are executed automatically
          then(resolve, reject) {
            // The final state is determined by the then method
            reject("It failed in the end.")}}}else {
        reject('fail')}},2000)})}const promise = getResponse('example')
promise.then(res= > console.log(res), err= > console.log(err)) // => Failed
Copy the code

Instance methods

A promise has three instance methods: then, catch, and finally

then

// The then method takes two arguments
Resolve === 1 - This parameter is optional
// Parameter 2 corresponds to executor reject when the rejected state is rejected. Parameter === Parameter 2 - this parameter is optional

// The then method converts the return value to a new Promise, so you can make chain calls
promise.then(res= > console.log(res), err= > console.log(err))
       .then(res= > console.log(res))
Copy the code

Then methods can be called multiple times, and the corresponding THEN methods are executed sequentially

const promise = getResponse('example')

promise.then(() = > console.log('success --- 1'))
promise.then(() = > console.log('success --- 2'))
promise.then(() = > console.log('success --- 3'))

/* => success --- 1 success --- 2 success --- 3 */
Copy the code
The return value of then
  1. The return value is plain (basic data type + plain object)
function getResponse(url) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url === 'example') {
        resolve('success')}else {
        reject('fail')}},2000)})}const promise = getResponse('example')


promise.then(res= > 123).then(res= > console.log(res)) / / = > 123

Then (res => new promise ((resolve, reject) => resolve(123))) */
Copy the code

The default return value for a function that does not return any value is undefined, so a then method that does not return any value returns a new promise instance, and a resolve(undefined) method that does not return any value.

  1. The return value is an instance of Promise
function getResponse(url) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url === 'example') {
        resolve('success')}else {
        reject('fail')}},2000)})}const promise = getResponse('example')


promise.then(res= > new Promise((resolve, reject) = > resolve('22222')))
       .then(res= > console.log(res)) / / = > 22222

Then (res => new promise ((resolve, reject)) => {resolve(new promise ((resolve, reject)) reject) => resolve('22222'))) })).then(res => console.log(res)) */
Copy the code
  1. The return value is a thenable object
function getResponse(url) {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      if (url === 'example') {
        resolve('success')}else {
        reject('fail')}},2000)})}const promise = getResponse('example')


promise.then(res= > ({
  then(resolved, rejected) {
    resolved(111111)
  }
})).then(res= > console.log(res)) / / = > 111111

Then (res => new promise (resolve, reject) => {resolve({resolve, rejected) { resolved(111111) } }) })).then(res => console.log(res)) */
Copy the code

catch

When we only want to capture the reject state, we typically set the first parameter of the THEN method to undefined

promise.then(undefined.() = > console.log('error'))
Copy the code

For ES6, the corresponding syntactic sugar form code is provided

promise.catch(() = > console.log('error'))
Copy the code

When a promise executes reject or throws an exception, the promise goes into the Rejected state, so the catch method is fired

function getResponse(url) {
  return new Promise((resolve, reject) = > {
    // When promise throws an exception -- the state switches to Rejected
    // The corresponding error object is passed as a reject parameter
    throw new Error('error')})}const promise = getResponse()

promise.catch(err= > {
  console.log('err', err)
})
Copy the code

If there are more than one catch method, the catch methods are executed sequentially

function getResponse(url) {
  return new Promise((resolve, reject) = > {
    reject('error')})}const promise = getResponse()

promise.catch(() = > console.log('err1'))
promise.catch(() = > console.log('err2'))
promise.catch(() = > console.log('err3'))
Copy the code

The return value of a PROMISE is still a promise

promise.then(() = > console.log('success'),() = > console.log('error'))
Copy the code

Can be converted to

promise.then(() = > console.log('success'))
       .catch(() = > console.log('error'))
Copy the code

Note:

function getResponse(url) {
  return new Promise((resolve, reject) = > {
    reject('error')})}const promise = getResponse()

// The next two lines are actually two calls to promise
// So for line 11, it implements only resolve, not reject
// So an error is reported when the promise state is Rejected
promise.then(() = > console.log('success'))
promise.catch(() = > console.log('error'))
Copy the code

If a catch corresponds to multiple promises, the catch catches the first reject promise and terminates the execution of all promises

function getResponse(url) {
  return new Promise((resolve, reject) = > {
    resolve('success')})}const promise = getResponse()

The catch method is raised when a reject promise is returned by the then method
promise
  .then(() = > new Promise((resolve, reject) = > reject('Wrong')))
  .catch(() = > console.log('reject')) // => reject 
Copy the code
function getResponse(url) {
  return new Promise((resolve, reject) = > {
    reject('error')})}const promise = getResponse()

The catch method is raised when the getResponse method returns a reject promise
promise
  .then(() = > new Promise((resolve, reject) = > reject('Wrong')))
  .catch(() = > console.log('reject')) // => reject
Copy the code

The return value of the catch method

The return value of the catch method is also a Promise instance, so the rule for the return value is the same as the rule for the return value of the resolve method

function getResponse(url) {
  return new Promise((resolve, reject) = > {
    reject('error')})}const promise = getResponse()

promise
  .catch(() = > {
    console.log('reject')
    return 'Return value of catch method'
  })
  // Because the return value of the catch method is also converted to a new Promise instance
  // So in this case, the then method after the catch method is also executed normally
  .then(res= > console.log(res)) // => The return value of the catch method
Copy the code

finally

Finally is a new feature in ES9(ES2018) : code that indicates that a Promise object will eventually be executed whether it becomes a pity or a Reject state

The finally method does not accept parameters because it executes whether the preceding state is a pity or reject state

function getResponse(url) {
  return new Promise((resolve, reject) = > {
    reject('error')})}const promise = getResponse()

promise
  .then(res= > console.log(res))
  .catch(err= > console.log(err))
  .finally(() = > console.log('Code that must be executed'))
Copy the code

Class method

resolve

The use of promise.resolve is equivalent to new Promise, and the resolve operation is performed

const promise = Promise.resolve({ name: 'Klaus' })
promise.then(res= > console.log(res))
Copy the code

Is equivalent to

const promise = new Promise((resolve, reject) = > resolve({ name: 'Klaus' }))
promise.then(res= > console.log(res))
Copy the code

The promise. resolve method can accept arguments of the type

  • A parameter is a common value or object
  • The argument itself is a Promise
  • The argument is a thenable

The specific judgment rules are the same as before

reject

Promise. Reject is used the same way as New Promise, except that the reject method is called

Reject is passed directly to the catch as a reject state parameter, regardless of its form

const promise = Promise.reject('reject')
promise.catch(res= > console.log(res))
Copy the code

Is equivalent to

const promise = new Promise((resolve, reject) = > reject('error'))
promise.catch(res= > console.log(res))
Copy the code

all

The idea is to wrap multiple promises together to form a new Promise

The new Promise status is determined jointly by all the promises in the package

  • When all the Promise states become the fulfilled state, the new Promise state becomes the fulfilled state, and all the Promise return values will be formed into an array
  • When a Promise is reject, the new Promise is REJECT and takes the return value of the first REJECT as an argument
const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > resolve('22222'), 2000))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))

The return value of the promise. all method is still a Promise instance
Promise.all([p1, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err))
// => ['11111', '22222', '33333']
Copy the code
const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > resolve('22222'), 2000))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))

// The order of the output results is determined by the order of the parameters, rather than by the promise which first transitions to a depressing state
// Even if P1 is determined as the state of depressing before P2, the result of P1 will still be output as the second value of the array
Promise.all([p2, p1, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err))
// => ['22222', '11111', '33333']
Copy the code
const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 2000))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))

// When a promise status is set to Rejected, a catch is entered
Promise.all([p2, p1, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err))
/ / = > 22222
Copy the code

allsettled

The method will come to a final state only when all the Promise is settled, whether this is a pity or a reject

The return value of the AllSettled method is also a promise, and the result of this promise must be a pity

const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 2000))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))

Promise.allSettled([p1, p2, p3])
  .then(res= > console.log(res))
  .catch(err= > console.log(err))
/* => [ { status: 'fulfilled', value: '11111' }, { status: 'rejected', reason: '22222' }, { status: 'fulfilled', value: '33333'}] * /
Copy the code

race

The arguments to the RACE method accept an array of promises and return a promise

The state of the returned promise is determined by the promise whose state is determined by the first one

If the state of the first stateful promise is Resolved, the returned promise is Resolved

If the first stateful promise is in the rejected state, then the returned promise is in the Rejected state

const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 2000))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))

Promise.race([p1, p2, p3])
  .then(res= > console.log('res', res))
  .catch(err= > console.log('err', err))
/* => res 11111 */
Copy the code
const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 500))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))

Promise.race([p1, p2, p3])
  .then(res= > console.log('res', res))
  .catch(err= > console.log('err', err))
/* => err 22222 */
Copy the code

any

The any method is new in ES12 and is similar to the Race method

The any method will wait for a depressing state before deciding the state of the new Promise

If all promises are reject, then you wait until all promises are rejected

If all promises are reject, an AggregateError is reported

const p1 = new Promise((resolve, reject) = > setTimeout(() = > resolve('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 500))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > resolve('33333'), 3000))

Promise.any([p1, p2, p3])
  .then(res= > console.log('res', res))
  .catch(err= > console.log('err', err))
/* => res: 11111 */
Copy the code
const p1 = new Promise((resolve, reject) = > setTimeout(() = > reject('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 500))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > reject('33333'), 3000))

Promise.any([p1, p2, p3])
  .then(res= > console.log('res', res))
  .catch(err= > console.log('err', err))
/* => err: AggregateError: All promises were rejected returns a AggregateError instance is equal to the Promise. Any internal reject method performs for reject (new AggregateError (' All promises  were rejected')) */
Copy the code
const p1 = new Promise((resolve, reject) = > setTimeout(() = > reject('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 500))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > reject('33333'), 3000))

// For AggregateError instances, we can use the errors method to get all the arguments that promise passes in the reject method
// The type is array
Promise.any([p1, p2, p3])
  .then(res= > console.log('res', res))
  .catch(err= > console.log(err.errors))
// => ['11111', '22222', '33333']
Copy the code
const p1 = new Promise((resolve, reject) = > setTimeout(() = > reject('11111'), 1000))
const p2 = new Promise((resolve, reject) = > setTimeout(() = > reject('22222'), 500))
const p3 = new Promise((resolve, reject) = > setTimeout(() = > reject('33333'), 3000))

// Of course, the output order of error messages still corresponds to the order of arguments passed in
// Instead of corresponding to the transition order of the promise state
Promise.any([p2, p1, p3])
  .then(res= > console.log('res', res))
  .catch(err= > console.log(err.errors))
// => ['22222', '11111', '33333']
Copy the code