1, Why introduce Promise
Before introducing this chapter, first raise a few questions:
- What problem did Promise solve?
- What are the specific usage scenarios for Promise?
What problem did Promise solve?
- Callback hell problem
Before Promise, the front end often needs to solve the asynchronous problem by nesting callback functions layer by layer to obtain data. For example, the following code example:
// Callback hell instance // Milk tea function function getTea(fn) { setTimeout(() => { fn('Get a cup of milk tea') },2000) } // Bread function function getBread(fn) { setTimeout(() => { fn('Get a bread') },100) } // If it must be obtained in order, rather than according to time, it is required to obtain milk tea first and then bread. getTea(function(data) { console.log(data); getBread(function(data) { console.log(data); }) })
- readability issues
Through Promise, we can rewrite the above code into the following way, which is obviously more readable.
// Let's explain how to solve the problem of callback hell through Promise function getTea() { return new Promise((resolve) => { setTimeout(() => { resolve('Get a cup of milk tea') }, 2000) }) } function getBread() { return new Promise((resolve) => { setTimeout(() => { resolve('Get a bread') }, 500) }) } getTea() .then(res => { console.log(res); return getBread(); }) .then(res => { console.log(res); })
- Trust problem (also known as callback multiple execution problem)
The traditional callback function cannot be guaranteed to be executed only once, and the callback function may also be used to perform other operations. However, Promise calls and only calls resolve once, which will not cause the problem of callback executing multiple times. Therefore, Promise solves the problem of callback calling multiple times by a third-party library.
What are the specific usage scenarios for Promise?
- Scenario 1: write the picture loading as a Promise. Once the picture is loaded, the Promise state will change.
- Scenario 2: when the next asynchronous request needs to rely on the result of the previous request, the problem can be solved through chain operation.
- Scenario 3: merge multiple requests through all() and summarize all the request results. Just set one loading.
- Scenario 4: you can set the timeout of image request through race().
2, The method of handwritten Prromise
Handwritten promise all
Promise.all is characterized by receiving an iteratable object. When all elements in the iteratable object are successfully executed, an array will be returned, and an error will be returned immediately.
function myPromiseAll(iterable) { // First, specify that the object to be returned is a Promise return new Promise((resolve,reject) => { // First, convert the iteratable object into an array const promises = Array.from(iterable); let flag = 0; const result = []; // Start traversal execution for (let i = 0; i < promises.length; i++) { Promise.resolve(promises[i]).then(res => { result[i] = res; flag++; if (flag === promises.length) { resolve(result) } }).catch(err => { reject(err) }) } }) }
Handwritten promise race
Promise. The race function receives an iteratable object, which is equivalent to racing all promise objects in the iteratable object. As long as the state of one promise object changes, it directly returns the result returned by the promise object.
// Handwritten promise race function myPromiseRace(iterator) { // The first thing to return is a promise object return new Promise((resolve,reject) => { for (let item of iterator) { Promise.resolve(item).then(res => { resolve(item); }).catch(err => { reject(err); }) } }) } let p1 = new Promise(resolve => { setTimeout(resolve, 105, 'p1 done') }) let p2 = new Promise(resolve => { setTimeout(resolve, 100, 'p2 done') }) myPromiseRace([p1, p2]).then(data => { console.log(data); // p2 done })
Handwritten promise finally
Promise.finally features
- This method is executed regardless of success or failure
- A Promise is returned
Promise.finally execution example
let p = new Promise((resolve,reject) => { setTimeout(() => { resolve(111); },2000) }) p.then(res => { console.log(res); // 111 }).finally(() => { console.log('It will be executed here anyway'); // It will be executed here anyway })
Handwritten promise Finally (Promise.finally is essentially a then method, which needs to execute the parameters we passed in and then return the formal parameters)
Promise.prototype.finally = function(f) { return this.then((value) => { return Promise.resolve(f()).then(() => value) },(err) => { return Promise.resolve(f()).then(() => { throw err; }) }) }
Promise.all and promise The difference between race
Promise. The return values of all() success and failure are different. When successful, it returns a result array, while when failed, it returns the first reject ed value. When promise When the result of all() is successful, return the data order and promise in the array of the result The promise order received by all() is consistent.
promise.race means multiple Promise races. Which result will be returned as soon as it is executed. No matter whether the result itself is successful or failed, other Promise codes will be executed, but will not be returned.
Promise.all and promise Application scenario of race
promise. Application scenario of all()
- The scene to display when multiple asynchronous tasks get results
For example, when the user clicks the button, a dialog box will pop up. The data in this dialog box comes from the data obtained by two different back-end interfaces. When the user just clicks, the state in data loading will be displayed. When these two parts of data are obtained from the interface, the state in data loading will disappear. At this time, promise can be used All method.
Promise. Application scenario of race()
- Prompt user request timeout
For example, when the user clicks the button to send a request, and the back-end interface has not obtained data beyond the time we set, we can prompt the user that the request times out.
3, How does Promise solve serial and parallel problems?
What is parallelism? What is serial?
Parallel: refers to the simultaneous execution of multiple asynchronous requests.
Serial: an asynchronous request is completed before the next request is made.
Promise implements parallel requests
Promise mainly relies on promise All method and promise Race method, we can promise by handwriting All method or promise Race method to achieve this goal.
Promise implements serial requests
Promise realizes serial request mainly with the help of reduce function. You can refer to my article How to control the serial execution of Promise?
// The serial execution of Promise is realized with the help of reduce function const funcArr = [ () => { return new Promise((resolve) => { setTimeout(() => {resolve(1)},2000) }) }, () => { return new Promise((resolve) => { setTimeout(() => {resolve(2)},1000) }) }, () => { return new Promise((resolve) => { setTimeout(() => {resolve(3)},3000) }) }, ]; function inOrder(arr) { const res = []; return new Promise((resolve) => { arr.reduce((pre,cur) => { return pre.then(cur).then(data => res.push(data)) },Promise.resolve()).then(data => resolve(res)) }) } inOrder(funcArr).then(data => console.log(data)) // [1,2,3]
4, What is Promise penetration?
The so-called Promise value penetration refers to then or The parameter of catch is expected to be a function. If it is not a function, value penetration may occur. The Promise method passes values through return. Without return, it's just independent tasks. Taking a look at the output of the following example may better help us understand what value penetration is?
Promise.resolve(1) .then(function(){return 2}) .then(Promise.resolve(3)) .then(console.log) // 2
The reason why value penetration occurs is that what is passed in the second then is not in the form of a function.
5, Encapsulating Ajax requests with Promise
The key steps of using Promise to encapsulate Ajax requests are all in the comments in the following code. Please see the following code for details.
// Encapsulating Ajax requests with Promise const res = new Promise((resolve,reject) => { // 1. Create an XMLHttpRequest object const xhr = new XMLHttpRequest(); // 2. Initialization request method and URL xhr.open('GET','https://api.apiopen.top/getJoke'); // 3. Send request xhr.send(); // 4. Bind the event and process the response result xhr.onreadystatechange = function() { if (xhr.readyState === 4) { // Here 4 means that the server returns all the results // If the status code returned by the server starts with 2, we will resolve the returned result. Otherwise, we will reject the corresponding status code if (xhr.status >= 200 && xhr.status < 300) { resolve(xhr.response) } else { reject(xhr.status) } } } }) res.then(function(value) { console.log(value); },function(err) { console.log(err); })
6, What are the states of Promise?
Promise mainly has the following three states:
- pending state (initial state)
- Completed status
- rejected status (status that has failed)
Promise state change process
- Switching from pending to fulfilled state
Before resolve, it is in pending state, and after resolve, it is in full state
const p = new Promise((resolve, reject) => { setTimeout(() => { console.log('resolve Status before:', p); resolve(); console.log('resolve Status after', p); }) })
- From pending state to rejected state
The status before reject is pending, and the status after reject is rejected.
const p = new Promise((resolve, reject) => { setTimeout(() => { console.log('reject Status before:', p); reject(); console.log('reject Status after', p); }) })
7, Rewrite callback to Promise
-
Traditional callback form
const fs = require('fs');
fs.readFile('./temp.md',(err,data) => {
console.log(data.toString());
}) -
Change the form of callback to promise
The core is to obtain callback data through resolve.
const fs = require('fs'); async function myReadFile() { let result = await new Promise((resolve,reject) => { fs.readFile('./temp.md',(err,data) => { resolve(data.toString()); }) }) console.log(result); // xxxxx return result; } myReadFile()