Promise usage review

Promise usage review

First, let's review what Promise is.

Promise is a solution for asynchronous programming, which is more reasonable and powerful than traditional solutions, callback functions and events.

Promise is simply a container that holds the results of an event (usually an asynchronous operation) that will not end in the future. Syntactically speaking, promise is an object from which you can get the message of asynchronous operation. Promise provides a unified API, and various asynchronous operations can be processed in the same way.

(1) Promise instances have three states:

Pending (in progress)
Resolved (completed)
Rejected
When a task is handed over to promise, its state is Pending. When the task is completed, it becomes Resolved, and when it fails, it becomes Rejected.

(2) Promise instance has two processes:

Pending - > fulfilled: resolved
Pending - > Rejected: Rejected
It should be noted that once the status changes from in progress to other status, the status can never be changed.

characteristic:

The asynchronous operation is expressed in the process of synchronous operation, which avoids the nested callback functions. The process is clearer and the code is more elegant.
Promise object provides a unified interface, which makes it easier to control asynchronous operations.

Disadvantages:

Promise cannot be cancelled. Once it is created, it will be executed immediately. It cannot be cancelled halfway.
If the callback function is not set, the error thrown by Promise will not be reflected to the outside.
When it is in the pending state, it is impossible to know which stage it has reached (just started or about to be completed).

example:

When we construct Promise, the code inside the constructor is executed immediately:

new Promise((resolve, reject) => {
  console.log('new Promise')
  resolve('success')
})
console.log('finifsh')
// new Promise -> finifsh

Promise implements a chain call, that is, after calling then every time, a promise is returned, and it is a brand-new promise. The reason is also that the state is immutable. If you use return in then, the value of return will be promise Resolve() packaging

Promise.resolve(1)
  .then(res => {
    console.log(res) // => 1
    return 2 // Packaged as promise resolve(2)
  })
  .then(res => {
    console.log(res) // => 2
  })

Of course, Promise also solves the problem of callback hell. You can rewrite the previous example of callback hell into the following code:

ajax(url)
  .then(res => {
      console.log(res)
      return ajax(url1)
  }).then(res => {
      console.log(res)
      return ajax(url2)
  }).then(res => console.log(res))

All the above are about some advantages and features of Promise. In fact, it also has some disadvantages. For example, Promise cannot be cancelled, and errors need to be captured through callback functions.

Promise Foundation

1. The execution result of the following code is

const promise = new Promise((resolve, reject) => {
  console.log(1);
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);

Finally, 1 2 4 should be output. The most important thing is 3. You should know promise Then is a micro task, which will not be executed until all macro tasks are executed. At the same time, the internal state of promise needs to be changed. Because there is no change here, 3 is not output.

2. The execution result of the following code is

const promise1 = new Promise((resolve, reject) => {
  console.log('promise1')
  resolve('resolve1')
})
const promise2 = promise1.then(res => {
  console.log(res)
})
console.log('1', promise1);
console.log('2', promise2);

The output results are as follows:

'promise1'
'1' Promise{<resolved>: 'resolve1'}
'2' Promise{<pending>}
'resolve1'

It should be noted that if you print promise1 directly, its status value and parameters will be printed.

Here is the specific idea of this problem:

  • . script is a macro task. Execute these codes in order. First enter Promise, execute the code in the constructor, and print Promise 1
  • Encounter the resolve function, change the state of promise1 to resolved, and save the result
  • Meet promise 1 Then this micro task is put into the micro task queue. Promise 2 is a new promise with the status of pending
  • Execute synchronization code 1, print out the status of promise1 as resolved, execute synchronization code 2, and print out the status of promise2 as pending
  • After the macro task is executed, find the micro task queue and find promise 1 Then execute the micro task with the status of resolved.

In this way, all the code is executed.

3. The execution result of the following code is

const promise = new Promise((resolve, reject) => {
  console.log(1);
  setTimeout(() => {
    console.log("timerStart");
    resolve("success");
    console.log("timerEnd");
  }, 0);
  console.log(2);
});
promise.then((res) => {
  console.log(res);
});
console.log(4);

  • First, if you encounter Promise constructor, you will execute the contents and print 1
  • steTimeout is encountered, which is a macro task and is pushed into the macro task queue
  • Next, continue to execute and print out 2
  • Since Promise is still pending at this time, Promise Then don't execute first
  • Continue to perform the following synchronization tasks and print out 4
  • The micro task queue has no tasks at this time. Continue to execute the next round of macro tasks and execute steTimeout
  • First, execute timerStart, and then encounter resolve. Change the state of promise to resolved, save the results, and replace the previous promise Then push the micro task queue and execute timerEnd
  • After executing the macro task, go to the micro task promise Then, print the result of resolve

4. The execution result of the following code is

Promise.resolve().then(() => {
  console.log('promise1');
  const timer2 = setTimeout(() => {
    console.log('timer2')
  }, 0)
});
const timer1 = setTimeout(() => {
  console.log('timer1')
  Promise.resolve().then(() => {
    console.log('promise2')
  })
}, 0)
console.log('start');

This topic is a little windy. Let's sort it out:

  • First, promise resolve(). Then is a micro task. Join the micro task queue
  • Execute timer1, which is a macro task and join the macro task queue
  • Continue to execute the following synchronization code and print out start
  • In this way, the first round of macro tasks will be executed. Start to execute micro tasks and print out promise1
  • timer2 is encountered, which is a macro task. Add it to the macro task queue
  • In this way, the first round of micro tasks is completed and the second round of macro tasks is started, which refers to the execution of timer timer1 and printing timer1
  • Promise is encountered. It is a micro task. Join the micro task queue
  • Start executing the tasks in the micro task queue and print promise 2
  • Finally, execute the macro task timer2 timer and print out timer2

This topic is still relatively complex, which is worthy of serious understanding.

Execution result:

'start'
'promise1'
'timer1'`Insert code slice here`
'promise2'
'timer2'

5. The execution result of the following code is

const promise = new Promise((resolve, reject) => {
    resolve('success1');
    reject('error');
    resolve('success2');
});
promise.then((res) => {
    console.log('then:', res);
}).catch((err) => {
    console.log('catch:', err);
})

The execution result is: then: success1

This topic examines that Promise's state will not change after it changes. The start state changes from pending to resolve, indicating that it has changed to completed state. The following two states will not be executed, and the following catch will not catch errors.

6. The execution result of the following code is

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)

The implementation results are:

1
Promise {<fulfilled>: undefined}

Promise. If the parameter of the resolve method is an original value or an object without the then method, promise The resolve method returns a new promise object with the status of resolved, promise The parameters of the resolve method will be passed to the callback function at the same time.

The parameter accepted by the then method is a function. If a function is not passed, it will actually interpret it as then(null), which will cause the result of the previous Promise to be passed to the following.

7. The execution result of the following code is

const promise1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 1000)
})
const promise2 = promise1.then(() => {
  throw new Error('error!!!')
})
console.log('promise1', promise1)
console.log('promise2', promise2)
setTimeout(() => {
  console.log('promise1', promise1)
  console.log('promise2', promise2)
}, 2000)

The output results are as follows:

promise1 Promise {<pending>}
promise2 Promise {<pending>}

Uncaught (in promise) Error: error!!!
promise1 Promise {<fulfilled>: "success"}
promise2 Promise {<rejected>: Error: error!!}

This is easier to understand, which is similar to the ideas of the above topics.

Promise's catch, then, finally

8. The execution result of the following code is

Promise.resolve(1)
  .then(res => {
    console.log(res);
    return 2;
  })
  .catch(err => {
    return 3;
  })
  .then(res => {
    console.log(res);
  });

The output result is: 1 2

Promise can be called in a chain, because every time it is called then or catch will return a new promise, which realizes the chain call. It is not like the chain call of our task, return this.

The reason why the above output results print out 1 and 2 in turn is that after resolve(1), the first then method is used instead of catch, so res in the second then actually gets the return value of the first then. And return 2 will be packaged as resolve(2) and printed out by the last then.

9. The execution result of the following code is

Promise.resolve().then(() => {
  return new Error('error!!!')
}).then(res => {
  console.log("then: ", res)
}).catch(err => {
  console.log("catch: ", err)
})

Any non promise value returned will be wrapped into a promise object, so the return new Error('error!!! ') here is also wrapped into a return promise resolve(new Error(‘error!!!’)).

Therefore, it is captured by then instead of catch, and the output result is:

"then: " "Error: error!!!"

10. The execution result of the following code is

const promise = Promise.resolve().then(() => {
  return promise;
})
promise.catch(console.err)

This is actually a pit then or The value returned by catch cannot be promise itself, otherwise it will cause an endless loop. So this question will report an error:

Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

11. The execution result of the following code is

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)

Seeing this topic, many then, in fact, we only need to remember one principle: Then or The parameter of catch is expected to be a function. If a non function is passed in, value transmission will occur.

Neither the first then nor the second then is a function. One is a number and the other is an object. Therefore, transparent transmission occurs, and the value of resolve(1) is directly transmitted to the last then.

So the output result is: 1

12. The execution result of the following code is

Promise.reject('err!!!')
  .then((res) => {
    console.log('success', res)
  }, (err) => {
    console.log('error', err)
  }).catch(err => {
    console.log('catch', err)
  })

Two parameters in the. then function:

The first parameter is the function used to handle Promise success
The second is the function that handles the failure

That is promise The value of resolve ('1 ') will enter the successful function, promise The value of reject ('2 ') will enter the failed function.

In this problem, the error is directly captured by the second parameter of then, so it will not be captured by catch. The output result is: 'error' 'error!!!'

However, if it is like this:

Promise.resolve()
  .then(function success (res) {
    throw new Error('error!!!')
  }, function fail1 (err) {
    console.log('fail1', err)
  }).catch(function fail2 (err) {
    console.log('fail2', err)
  })

If an error is thrown in the first parameter of then, it will not be inactivated by the second parameter, but captured by the following catch, so the output result is:

fail2 Error: error!!!
              at success 

13. The execution result of the following code is

Promise.resolve('1')
  .then(res => {
    console.log(res)
  })
  .finally(() => {
    console.log('finally')
  })
Promise.resolve('2')
  .finally(() => {
    console.log('finally2')
  	return 'I am finally2 Returned value'
  })
  .then(res => {
    console.log('finally2 hinder then function', res)
  })

**finally() * * it is rarely used. Just remember the following points:

The. finally() method executes regardless of the final state of the Promise object
The callback function of the. finally() method does not accept any parameters, that is, you are In the finally() function, it is impossible to know whether the final state of Promise is resolved or rejected
It will eventually return the default value of the last Promise object, but if an exception is thrown, it will return the Promise object of the exception.
finally is essentially a special case of then method

The output result of the above code is:

1
finally2
finally
finally2 hinder then Function 2

Open it again Error capture for finally():

Promise.resolve('1')
  .finally(() => {
    console.log('finally1')
    throw new Error('I am finally Exception thrown in')
  })
  .then(res => {
    console.log('finally hinder then function', res)
  })
  .catch(err => {
    console.log('Capture error', err)
  })

The output result is:

'finally1'
'Capture error' Error: I am finally Exception thrown in

Promise's all and race

The function of. all() is to receive a group of asynchronous tasks, then execute asynchronous tasks in parallel, and execute the callback only after all asynchronous operations are executed.

The function of. race() is to receive a group of asynchronous tasks, and then execute asynchronous tasks in parallel. Only the result of the asynchronous operation completed by the first execution is retained. Other methods are still executing, but the execution result will be discarded.

14. The execution result of the following code is

function runAsync (x) {
    const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
    return p
}

Promise.all([runAsync(1), runAsync(2), runAsync(3)]).then(res => console.log(res))

First, we define a Promise to asynchronously execute the function runAsync, which passes in a value of x, and then prints out the x after an interval of one second.

Then use promise All to execute this function. The results are as follows:

1
2
3
[1, 2, 3]

When executing, I see that 1, 2 and 3 are output one second later, and the array [1, 2 and 3] is output at the same time. The three functions are executed synchronously, and all results are returned in a callback function. And the result is consistent with the execution order of the function.

15. The execution result of the following code is

function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
function runReject (x) {
  const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))
  return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)])
       .then(res => console.log(res))
       .catch(err => console.log(err))

Output result:

// Output after 1s
1
3
// Output after 2s
2
Error: 2
// Output after 4s
4

We can see. catch caught the first error. The first error in this topic is the result of runReject(2).

If there is an exception in a group of asynchronous operations, it will not enter In the first callback function parameter of then(). Will be The second callback function of then().

16. The execution result of the following code is

Let's take another look at race:

function runAsync (x) {
  const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))
  return p
}
Promise.race([runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log('result: ', res))
  .catch(err => console.log(err))

Execution result:

1
'result: ' 1
2
3

Then will only capture the first successful method. Although other functions will continue to execute, they are not captured by then.

17. The execution result of the following code is

function runAsync(x) {
  const p = new Promise(r =>
    setTimeout(() => r(x, console.log(x)), 1000)
  );
  return p;
}
function runReject(x) {
  const p = new Promise((res, rej) =>
    setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x)
  );
  return p;
}
Promise.race([runReject(0), runAsync(1), runAsync(2), runAsync(3)])
  .then(res => console.log("result: ", res))
  .catch(err => console.log(err));

The output result is:

0
Error: 0
1
2
3

You can see that after catch catches the first error, the following code is not executed, but it will not be caught again.

Note: if there are asynchronous tasks that will throw exceptions in the array passed in by all and race, only the first error thrown will be caught, and it will be caught by the second parameter of then or the following catch; But it will not affect the execution of other asynchronous tasks in the array.

Async,await

When it comes to Promise, I have to mention async and await, which are also used to execute asynchronous code.

18. The execution result of the following code is

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2");
}
async1();
console.log('start')

The output result is:

async1 start
async2
start
async1 end

  • First execute the synchronization code async1 start in the function, and then encounter await, which will block the execution of the code after async1, so it will execute the synchronization code in async2 first
  • async2, then jump out of async1
  • After jumping out of the async1 function, execute the synchronization code start
  • End: after all the macro tasks in a round are executed, execute the content after await async1

It can be understood here that the statement after await is equivalent to being placed in new Promise, and the statements on the next line and after are equivalent to being placed in promise Then.

19. The execution result of the following code is

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
  setTimeout(() => {
    console.log('timer1')
  }, 0)
}
async function async2() {
  setTimeout(() => {
    console.log('timer2')
  }, 0)
  console.log("async2");
}
async1();
setTimeout(() => {
  console.log('timer3')
}, 0)
console.log("start")

The output result is:

async1 start
async2
start
async1 end
timer2
timer3
timer1

This topic is a little troublesome. Take a look at the steps:

  • First, enter async1 and print out async1 start. There is no doubt about this
  • After that, you encounter async2, enter async2, encounter timer timer2, join the macro task queue, and then print async2
  • Because async2 blocks the execution of the following code, execute the following timer timer3, add it to the macro task queue, and then print start
  • Then execute the code behind async2, print out async1 end, encounter timer timer1, and add it to the macro task queue
  • Finally, there are three tasks in the macro task queue, in the order of timer2, timer3 and timer1. There are no micro tasks, so all macro tasks are directly executed according to the principle of first in first out

In fact, it's not very difficult. As long as you clarify the event cycle mechanism, it's easy to do it!

20. The execution result of the following code is

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
  })
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

Output result:

script start
async1 start
promise1
script end

It should be noted here that Promise after await has no return value in async1, that is, its state is always pending, so the contents after await will not be executed, including those after async1 then.

21. The execution result of the following code is

async function async1 () {
  console.log('async1 start');
  await new Promise(resolve => {
    console.log('promise1')
    resolve('promise1 resolve')
  }).then(res => console.log(res))
  console.log('async1 success');
  return 'async1 end'
}
console.log('srcipt start')
async1().then(res => console.log(res))
console.log('srcipt end')

Here, the above question is modified and resolve d is added to see the output results:

script start
async1 start
promise1
script end
promise1 resolve
async1 success
async1 end

This is not difficult to understand, not much explanation.

22. The execution result of the following code is

Let's look at an interview question:

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}

async function async2() {
  console.log("async2");
}

console.log("script start");

setTimeout(function() {
  console.log("setTimeout");
}, 0);

async1();

new Promise(function(resolve) {
  console.log("promise1");
  resolve();
}).then(function() {
  console.log("promise2");
});
console.log('script end')

Output result:

script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout

23. The execution result of the following code is

async function async1 () {
  await async2();
  console.log('async1');
  return 'async1 success'
}
async function async2 () {
  return new Promise((resolve, reject) => {
    console.log('async2')
    reject('error')
  })
}
async1().then(res => console.log(res))

The output result is:

async2
Uncaught (in promise) error

We can see that if an error is thrown in the async function, the error result will be terminated and the execution will not continue downward.

If you want the code behind the error and deficiency to execute, you can use catch to catch:

async function async1 () {
  await Promise.reject('error!!!').catch(e => console.log(e))
  console.log('async1');
  return Promise.resolve('async1 success')
}
async1().then(res => console.log(res))
console.log('script start')

The output result is:

script start
error!!!
async1
async1 success

Other topics

24. The execution result of the following code is

const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(() => {
            console.log(5);
            resolve(6);
            console.log(p)
        }, 0)

 1. List item

        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg);
    });
}));
first().then((arg) => {
    console.log(arg);
});
console.log(4);

This is a comprehensive topic. Let's take a look at the implementation results:

3
7
4
1
2
5
Promise{<resolved>: 1}

Let's talk about the steps:

  • First enter Promise and print out 3, then enter Promise below and print out 7
  • A timer was encountered and added to the macro task queue
  • Execute resolve in Promise p, the status changes to resolved, and the return value is 1
  • Execute resolve in Promise first, the status changes to resolved, and the return value is 2
  • Encounter p.then, add it to the micro task queue, and encounter first() Then, add it to the task queue
  • Execute the external code and print out 4
  • In this way, the first round of macro tasks will be executed, and the tasks in the micro task queue will be executed. 1 and 2 will be printed successively
  • In this way, the micro task is completed and the next round of macro task is started. There is a timer in the macro task queue. Execute it and print 5. Since the execution has changed to the resolved state, so
  • resolve(6) will no longer execute
  • Finally, console Log § print Promise{: 1}

25. The execution result of the following code is

const async1 = async () => {
  console.log('async1');
  setTimeout(() => {
    console.log('timer1')
  }, 2000)
  await new Promise(resolve => {
    console.log('promise1')
  })
  console.log('async1 end')
  return 'async1 success'
} 
console.log('script start');
async1().then(res => console.log(res));
console.log('script end');
Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .catch(4)
  .then(res => console.log(res))
setTimeout(() => {
  console.log('timer2')
}, 1000)

The output result is:

script start
async1
promise1
script end
1
timer2
timer1                     

This question is relatively simple. Let's briefly talk about the implementation process:

  • First, execute the timing belt and print out script start
  • Timer timer1 encountered and added it to the macro task queue
  • Then execute Promise and print out Promise 1. Since Promise has no return value, the following code will not be executed
  • Then execute the synchronization code and print out script end
  • Continue to execute Promise below then and catch expects that the parameter is a function, and a number is passed in here. Therefore, value penetration will occur, and the value of resolve(1) will be passed to the last
  • then, print 1 directly
  • When the second timer is encountered, add it to the micro task queue, execute the micro task queue, and execute two timers in sequence. However, due to the timer time, it will be hit first after two seconds
  • Print out timer2 and print out timer1 after four seconds

26. The execution result of the following code is

const p1 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('resolve3');
    console.log('timer1')
  }, 0)
  resolve('resovle1');
  resolve('resolve2');
}).then(res => {
  console.log(res)  // resolve1
  setTimeout(() => {
    console.log(p1)
  }, 1000)
}).finally(res => {
  console.log('finally', res)
})

The implementation results are:

resolve1
finally  undefined
timer1
Promise{<resolved>: undefined}

It should be noted that p1 printed by the last timer is actually Finally, we know the return value If no error is thrown, the return value of finally will be the return value of the previous Promise by default Finally, the last Promise was But this one then() does not return a value, so the Promise value printed by p1 will be undefined. If a return 1 is added to the bottom of the timer, the value will become 1.

Keywords: Front-end ECMAScript Vue.js

Added by michalurban on Mon, 28 Feb 2022 09:12:26 +0200