1. What is asynchronization?
Take life as an example, it’s dinner time, we cook first, and then put the rice into the pot, we can’t stay here until it’s cooked before cooking, right? It was silly (no one would do that in real life), so we set a timer for our rice cooker to beep us when our meal was ready. So we don’t have to wait here for the rice to be cooked. After cooking the rice, we go to fry the food immediately. Then when the food is cooked, the rice cooker over there comes to inform us that the rice is cooked, and then we can start cooking. This is an asynchronous process simple, if in programming, and synchronize tasks and asynchronous tasks, Javascript is a single-threaded programming language, so will involve a lot of asynchronous function, the above example, if the rice has been waiting here rice boiled not to do other things, creates a block, we can’t do other things, We can’t do anything else until the meal is cooked, and the way to block is through asynchronous callback functions. So our cooking event is an asynchronous event, and then the cooker notifies us on a regular basis which is a callback function, so we get notified when the food is ready, and then we go back to the cooking event, and while the food is not ready, we can do other things. This is called asynchrony.
2. What is asynchrony in Javascript?
The simplest way to think about it is an event that takes time to wait for, as long as it does not block and can be handled by a callback function after completion, then it is an asynchronous event, such as setTimeout. Let’s look at some code
(function cook() {
console.log('Start cooking')
// The arrow function in setTimeout is equivalent to the callback function
setTimeout(() = > {
// Execute after 5 seconds
console.log('The meal is done')},5000)
})()
console.log('Start cooking')
Copy the code
We execute the cook function with the immediate execute function, and the final result printed on the console is start cooking -> start cooking -> cooked in this case, let’s say it takes 5 seconds to cook. So setTimeout takes 5 seconds to print when the meal is cooked. According to our normal thinking, the code is executed from top to bottom, if setTimeout here 5 seconds, we need to wait to execute the following start cooking, then it will cause a block, obviously work efficiency will be greatly reduced. So it needs to be handled asynchronously. After 5 seconds, it will execute the console.log in the setTimeout arrow function (‘ Dinner is done ‘).
Let’s look at one more piece of code, take Ajax for example, and go straight to the code
console.log('Start requesting data')
$.ajax({
// Suppose this is the address of the interface requesting data
url: "xxxxxx/api/list".// This function is a callback to the success of the data
success: function(data) {
// Execute only when the data is successfully obtained
console.log('Get the data successfully' + data)
}
});
console.log('Go do something else')
Copy the code
In this case, an Ajax request for data is also an asynchronous method, and the final printed result is to start requesting data -> do something else -> successfully get the data… Asynchronous events, however, take some time to complete, such as the following
console.log('Let's do the first thing.')
setTimeout(() = > {
console.log('Let's do the second thing.')},0)
console.log('Let's do the third thing.')
Copy the code
In this case, the printed result is to start doing the first thing -> start doing the third thing -> start doing the second thing why setTimeout set to 0 is not executed sequentially? (For the record, in the browser environment, setTimeout is set to 0 and there is a 4ms delay in execution.) So it is still an asynchronous event. As for why, it is necessary to go deeper into the Js event loop mechanism. Have you gained a clearer understanding of asynchronous events by now?
3. Callback Hell
Let’s use two examples in code to see what callback hell is
setTimeout(() = > {
console.log('Shopping for vegetables')
setTimeout(() = > {
console.log('It's cooking.')
setTimeout(() = > {
console.log(It's time to eat.)
setTimeout(() = > {
console.log('Wash the dishes.')},1000)},1000)},1000)},1000)
Copy the code
When we eat, we buy food first and then cook. When the food is cooked and eaten, we wash the dishes. That is to say, the next event has to wait for the completion of the next event before starting. This code will be printed out to buy food -> cook food -> eat food -> wash dishes but does this code feel ugly? It forms four levels of nesting, and if you nested a little more, you would have this symbol > a pointed triangle. Let’s look at another example of ajax requesting data
$.ajax({
url: "xxxxx/api/xxx".success: function(data1) {
console.log('First data requested', data1)
$.ajax({
url: "xxxxx/api/xxx".success: function(data2) {
console.log('Second data requested', data2)
$.ajax({
url: "xxxxx/api/xxx".success: function(data3) {
console.log('Third data requested', data3) } }); }}); }});Copy the code
Why nesting like this, you might ask? Because requesting the next piece of data requires some processing on top of the previous piece of data to continue the request. That is, requests for the third data depend on the second data, and requests for the second data depend on the first data, creating callback hell if they are nested. According to my understanding, will arrive at the 18th floor, nested continues to hell (gnome male – “, congratulations you arrived in hell, because each nested inside may have a lot of code, your code after you estimate also very difficult to understand) hell is empty, the devil here on earth, you write the code others see the estimate will also want to kill you this monster (why do you want to hurt us?)
4. A solution (Promise)
Let’s start with Promise. First, it’s something in ES6 that elegantly solves the callback hell problem above. A Promise is a Promise that will give you results over time. A Promise is a Promise that I will do over time. Promise has three states: Pending, Resolved and Rejected. Once the state has changed, it will never change.
Let’s go straight to the code and see how to use promises. First, we need to wrap a Promise around an asynchronous function (remember: any method that executes asynchronously can be wrapped as a Promise).
/* Encapsulate setTimeout as a dinner function, passing in the event argument */
function dinner(event) {
/* Create a promise object, resolve on success, reject on failure */
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(console.log(event))
}, 1000)})}/* How to use this function, there are two ways to call it */
// The first call
dinner('Shopping for vegetables')
.then(dinner('Cook dinner'))
.then(dinner(It's time to eat.))
.then(dinner('Wash the dishes.'))
// The second call
let res1 = dinner('Shopping for vegetables')
let res2 = res1.then(dinner('Cook dinner'))
let res3 = res2.then(dinner(It's time to eat.))
let res4 = res3.then(dinner('Wash the dishes.'))
Copy the code
The result is the same as the nested call above: buy food -> cook food -> eat food -> wash dishes
Let’s use the Ajax example again for Promise encapsulation
// Encapsulate
function myAjax(url) {
return new Promise((resolve, reject) = > {
$.ajax({
url: url,
success: function(data) {
// Data is returned on success
resolve(data)
},
error: function(err) {
// An error message, err, is returnedreject(err) } }); })}/* After wrapping up ajax, we can request data */
let res1 = myAjax('xxxx/api/xxxx');
let res2 = res1.then(myAjax('xxxx/api/xxxx'));
let res3 = res2.then(myAjax('xxxx/api/xxxxx'));
Copy the code
Res1 receives the data from the first interface, res2 relies on the data from the first interface, so use res1. Then to request the data from the second interface, res3 relies on the data from the second interface. I can use.catch to catch an error message, something like that
myAjax('xxxx/api/xxxx')
.then(res => {
// Request successfully executed here. Print the returned data successfully
console.log(res)
})
.catch(err => {
// Request failed to execute here. Print error message
console.log(err)
})
Copy the code
At this point, do you understand the basic use of Promise?
All and promise.race Promise. All: As the name suggests, is to request all of the Promise methods at once to see how they work
// Make the Promise wrapper first
function dinner(event) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(event)
}, 1000)})}// Use a variable to reference the Promise method
let p1 = dinner('Shopping for vegetables')
let p2 = dinner('Cook dinner')
let p3 = dinner(It's time to eat.)
let p4 = dinner('Wash the dishes.')
// The above four Promise methods are executed at once
Promise.all([p1, p2, p3, p4])
.then(res= > {
console.log(res)
})
Copy the code
If we look at the final result, we can see that it returns an array.Promise.all
If an error occurs, add a.catch () to the.then()
Promise.race: As the name suggests, it is a race that returns the result of whichever asynchronous method executes first, and returns only one result, not an array. Let’s do another example
function buy(event) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(event)
}, 2000)})}function eat(event) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(event)
}, 1000)})}function wash(event) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(event)
}, 3000)})}let p1 = buy('Shopping for vegetables')
let p2 = eat(It's time to eat.)
let p3 = wash('Wash the dishes.')
Promise.race([p1, p2,p3])
.then(res= > {
console.log(res)
})
Copy the code
In this case, let’s assume that it took 2 seconds to buy vegetables, 1 second to eat, and 3 seconds to wash dishes. The final result of course is printed out to eat, because eating is the fastest way to eat. Do you understand the basic use of Promise so far?
You think this is where it ends? Don’t worry, there’s still good stuff! Keep reading at 👇
5. Better use async await
In ES8, async await syntax has been added to make Promise calls more synchronous than.then. This code can be directly copied to your compiler to test!!
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
</body>
<script>
/* In this case, we directly use the outermost layer of nesting to execute the function immediately and write async */ at the beginning of the function
(async function() {
function buy(event) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
console.log(event)
resolve(event)
}, 2000)})}function eat(event) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
console.log(event)
resolve(event)
}, 1000)})}function wash(event) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
console.log(event)
resolve(event)
}, 3000)})}/* Before using await, we must write async before its outermost function otherwise we will get an error */
let p1 = await buy('Shopping for vegetables')
let p2 = await eat(It's time to eat.)
let p3 = await wash('Wash the dishes.')
})()
</script>
</html>
Copy the code
First of all, it takes 2 seconds to buy vegetables, 1 second to eat and 3 seconds to wash dishes. If we use the normal call (delete the “await” inside), then the final print result will be eating -> buying vegetables -> washing dishes because the one that takes less time must be printed first.
But ha, once you precede the Promise method with await, the next Promise method will wait for the previous Promise method to complete. As in this case, the result will be “buy groceries”, “eat meals”, “wash dishes”, thus fulfilling our original statement. The execution of the next result depends on the last result. In this case, there is no obvious feeling that it is much cleaner than the above methods, and it is more synchronous writing.