preface
When it comes to asynchronous Javascript programming, one of the most important aspects of front-end practice is that it evolves very quickly from the initial callback function to the later callback function. Why do I say that? Because despite all the syntactic evolution, evolution, and ultimately a return to callback, the most popular asynchronous programming solutions can be divided into four basic categories
- Callback Asynchronous solution (callback function)
- Promise asynchronous solution
- Async await syntax sugar
- Generator Asynchronous solution (traversal object Generator)
Before we share asynchronous programming, let’s first understand what is synchronous and what is asynchronous
Synchronous mode
The plain language description may not be easy to understand, but let me show you a little code
console.log(1)
function hs () {
console.log(3)}function hs2 () {
console.log(2)
hs()
}
hs2()
console.log(4)
1,2,3,4
Copy the code
Since Js is a single thread, during execution, synchronous tasks and asynchronous tasks enter different threads. Synchronous tasks enter the main thread, and asynchronous tasks enter the Event Table. When the specified asynchronous task is completed, Event Table moves this function to the Event Queue. When the main thread is empty, the Event Queue reads the corresponding function and enters the main thread for execution. This process is repeated over and over again, known as an Event Loop. For those who are interested, check out my other article to learn more about Js event loops and eventloops
So in this example of synchronous mode, you just go straight into the main thread and execute from top to bottom, so the output is: 1,2,3,4
Asynchronous mode
Again, a little code
console.log(1)
setTimeout(function hs () {
console.log(2)},2000)
setTimeout(function hs2 () {
console.log(3)
setTimeout(function hs3 () {
console.log(4)},3000)},1000)
console.log(5) The result believes many children shoes also can say1.5.3.2.4
Copy the code
How does it work? This is the event loop expressed above. Let’s go through the logic (regardless of the global call stack)
- The Js single thread executes from top to bottom, going to console.log(1) and pushing > execute the call stack
Print 1
> the stack - Then, go to the first setTimeout to perform the push operation > execute the call stack (execute the hs function after registering Event 2s in the Event Table) > unstack
Notice that no code is executed, just that the corresponding events are registered in the event list
- Then continue down to the second setTimeout to perform the push operation > execute the call stack (perform hs2 function after registering the Event in the Event Table for 1s) > unstack
Do the same as step 2
- Finally, go to console.log(5). Similarly, push > Execute call stack
Print 5
> the stack - At this point, the entire sample code, there’s no code on the call stack to execute, and the console prints one, five
The call stack is gone, but we still have it in our event list, step 2,3
- So what Js is going to do, it’s going to move the registered events from the Event Table to the Event Queue according to the order in which they were executed, to the Event Queue, to the Event Queue, to what, here comes the hero
Event Loop
Event loop, soEvent Loop
When will it be implemented? Look at Step 5, there are no more tasks in the call stack to execute, and the corresponding function execution will be read from the event queue, ok - through
Event loop
Event loop from event queueEvent Queue
To read the corresponding function into the execution stack - The hs2 function will be executed after 1s
Print 3
(Execute hs3 function after registering Event 3S in Event Table) > unstack - 2 seconds after the hs function, push > execute the call stack
Print 2
> the stack - At this point, there is no code in the call stack to execute, again, look in the event list
- Alas, there is a 3s after the execution function HS3, move into the event queue, through the event loop, push > call execution stack
Print 4
> the stack - over
These 12 steps describe the execution rules of the Event Loop in detail, and also clearly express the execution logic of the above code. After careful reading twice, you will definitely see, haha, I am a lazy person and do not like drawing, So ~
OK, through two simple examples, understand the difference between synchronous and asynchronous, back to the topic, what are the asynchronous solutions, how are they implemented, let’s discuss one by one
The callback function
Suppose we need to request an interface, and then render the data that the interface returns to the page; Normally we would write render logic in the success callback. In order to make the logic code more concise and clear, the return function plays a very important role
General idea coding
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if(this.readyState === 4) {if(this.status === "200") {
// Get the this.responseText return value
// do your own business logic
}
}
}
xhr.open("get"."http://www.xxx.com")
xhr.send()
Copy the code
The idea of a callback function
What does it mean to return to a function? In asynchro, go back and call the function you defined earlier, and I’m going to simulate this in a simple way, wrapping an Ajax request, passing in parameters to request address url and callback
function myAjax (url,callback){
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if(this.readyState === 4) {if(this.status === "200") {
callback(this.responseText)
}
}
}
xhr.open("get",url)
xhr.send()
}
Copy the code
How to use it?
myAjax("http://www.xxx.com".function (res) {
// Get the return value and do the logic
console.log(res)
})
Copy the code
Is it clear at a glance, simple business logic, transparent, so it is really good? Let’s change the scenario, suppose I need to ask for a specific address, logical city by province, search for city, search for district by city, search for street by district, search for specific address by street, ok, logical, write down the code
myAjax("http://www. Get province.com".function (res) {
// Ignore judgment
myAjax("http://www. Get city.com? Id = province ID".function (res) {
// Ignore judgment
myAjax("http://www. Get zone.com? Id = market ID".function (res) {
// Ignore judgment
myAjax("http://www. Get street.com? Id = district ID".function (res) {
// Ignore judgment
myAjax("http://www. Get street.com? Id = street ID".function (res) {
// OK to query the detailed address})})})})})Copy the code
It’s thoughtful, regular, but it doesn’t look very elegant. This is the myth of callback hell, relying on the results of the previous level to do the next thing, so Promise comes in
Promise
What is Promise? It is primarily designed to solve callback hell, and is currently one of the leading asynchronous solutions
Detailed understanding of Promise use and principle, fast track, handwritten Promsie
So here’s a simple example of how it works, right?
new Promise((resolve,reject) = > {
setTimeout(() = > {
resolve("hellow promise")},1000)
})
.then(
res= > { console.log(Successful `${res}`)})Promise {<pending>} VM696:6Successful hellow promise// Prints after 1s
Copy the code
New a Promise instance, then call the success callback resolve to change the state and trigger the success callback then method. We use Promsie to do a simple optimization for the above province, city, district, street, and detailed address
First, we’ll rewrite the myAjax method. Instead of passing in a callback, we’ll simply return a promise in the myAjax method and trigger the promise’s success callback after the request succeeds. If you don’t know how to use a Promise, you need to learn how to use a Promise or write a Promise by hand in the link article above
function myAjax (url){
return new Promise((resolve,reject) = > {
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if(this.readyState === 4) {if(this.status === "200") {
resolve(this.responseText)
}
}
}
xhr.open("get",url)
xhr.send()
})
}
Copy the code
Well, with one small change to myAjax, our actual code has changed substantially
myAjax("http://www. Get province.com")
.then(res= > {
// res.resid (province ID)
return myAjax("http://www. Obtain market.com? Id =resId")
})
.then(res= > {
// res.resid
return myAjax("http://www. Get area.com? Id =resId")
})
.then(res= > {
// res.resid (ID)
return myAjax("http://www. Get street.com? Id =resId")
})
.then(res= > {
// res.resid (street ID)
return myAjax("http://www. Obtain detailed address. Com? Id =resId")
})
.catch(err= > {
// Exception handling
})
Copy the code
Then chain calls. Is there a better way to solve this problem? The answer is of course Async await
Async await
ES2017 introduced async, so what is it and what does it solve? It’s just a syntactic sugar for a Generator function. What is a Generator function? Two things async await and Generator are raised here
In fact, async functions are not very different from ordinary functions. In use, they are basically the same. Just add an async keyword in front of the function declaration
function foo () {return "I'm a normal function."}
foo() // "I am a normal function"
async function foo () {return "I'm async."}
foo() // Promise {< big pity >: "I am async function "}
Copy the code
Through a short code, it can be known that the ordinary function execution will directly return the function result, while the async function execution will return a promise and the default state is fulfilled successfully, which will return the successful result. Therefore, in the face of the async function execution, We can externally obtain the result of a function by using.then, like this
async function foo () {return "I'm async."}
foo() // Promise {< big pity >: "I am async function "}
foo().then(c= > {console.log(c)})
// I am async
Copy the code
The foo function entity above essentially returns a promise, hence the code below
async function foo () {
return new Promise( resolve= > {
resolve("I'm async.")
})
}
foo().then(c= > {console.log(c)})
// I am async
Copy the code
After async, what is the use of await? Async means that there is an asynchronous operation in a function, and await means that the following expression needs to wait for the result to be used
Use a PROMISE + setTImeout to simulate asynchronous sleep delay. Await can also be followed by an asynchronous expression, such as a network request
/ / sleep function
let sleep = time= > new Promise(f= > setTimeout(f,time))
async function foo () {
await sleep(2000)
console.log("I came in 2 seconds later.")
return "I came in 2 seconds later."
}
foo()
// execute console.log(" I came in after 2s ")
// Extension: the result returned by async can be called back via the then method
foo().then(c= > {console.log(c)})
// 2 seconds later I came in
Copy the code
To sum up, write asynchronous tasks in synchronous mode!
Ok, after a brief look at the use of async await, let’s go back to optimizing a version of the province, city, district, street, and detailed address
The original myAjax wrapper remains the same, because the async function execution itself returns a Promise, and myAjax returns a Promise, which is fine
function myAjax (url){
return new Promise((resolve,reject) = > {
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
if(this.readyState === 4) {if(this.status === "200") {
resolve(this.responseText)
}
}
}
xhr.open("get",url)
xhr.send()
})
}
Copy the code
So it is our business code block that has been optimized, leaving out request failures and exception handling
// Define a method that executes Async functions
async function excutorAsync () {
let s0 = await myAjax("http://www. Get province.com")
let s1 = await myAjax("http://www. Get market.com? Id ="+s0)
let s2 = await myAjax("http://www. Get zone. Com? Id ="+s1)
let s3 = await myAjax("http://www. Get street.com? Id ="+s2)
let s4 = await myAjax("http://www. Obtain detailed address. Com? Id ="+s3)
}
Copy the code
What the hell is that? Take it easy. Let’s get this straight
- Let s0 = myAjax(“http://www. Get province.com”) The myAjax method returns a promise
- Let s0 = new Promsie(resolve => resolve)
- add
await
: let s0 = await new Promsie(resolve => resolve(” result of successful request “)) - The first example of async function is described above. Mark the paragraph in red and execute the async function to return the successful result of a promise default state fulfilled
- So: let s0 = (await success result of asynchronous expression, save Id)
- Similarly, asynchronous tasks are executed from top to bottom in sequential synchronous mode
Emmm ~ over, this looks a lot more optimistic.
The Generator function
We have just said that async await is the syntactic sugar of Generator functions. Now that we have sugar, the original Generator will be gradually forgotten by the public. Therefore, it is missing in the current mainstream asynchronous solutions of Js, and is gradually replaced by async await. Today we’ll also look briefly at its use
Generator functions are an asynchronous programming solution provided by ES6 with two minor differences from normal functions.
function
There is an asterisk * between the keyword and the function name- Function body for internal use
yield
expression
Let’s do a little code
function* foo () {
yield "hello"
yield "Grenerator"
return "over"
}
foo() // foo {<suspended>}
let g = foo()
g.next() // {value: "hello", done: false}
g.next() // {value: "Grenerator", done: false}
g.next() // {value: "over", done: true}
Copy the code
It’s interesting to look at the print. If you execute foo separately, you don’t execute any code. Instead, you return a pointer to an internal state (Iterator Object).
Quoting teacher Ruan Yifeng’s description, it is necessary to call the next method of the traverser object to make the pointer move to the next state. That is, each time the next method is called, the internal pointer executes from the head of the function or where it was last stopped until the next yield expression (or return statement) is encountered. In other words, Generator functions are executed piecewise, yield expressions are paused tokens, and the next method can resume execution.
So what does the return value represent after executing the next method on the iterator object?
{
value: "hello".// value: yield the result of the expression behind it
done: false // done: indicates whether traversal is complete
}
Copy the code
After analysis, understand its execution logic and rules, and then return to the above example, to practice
function* foo () {
let s0 = yield myAjax("http://www. Get province.com")
let s1 = yield myAjax("http://www. Get market.com? Id ="+s0)
let s2 = yield myAjax("http://www. Get zone. Com? Id ="+s1)
let s3 = yield myAjax("http://www. Get street.com? Id ="+s2)
let s4 = yield myAjax("http://www. Obtain detailed address. Com? Id ="+s3)
return s4
}
var g = foo()
/ / define
function co (res) {
if(res.done) return;
res.value.then(data= > {
co(g.next())
})
}
co(g.next())
Copy the code
- To define a
Generator
The function foo - Write down the logical code for the request along the lines of synchronization
- Gets the traverser object
g
- Defines iterator object looping execution methods
co
, first callco(g.next())
- Each call
co(g.next())
According to themyAjax
Method encapsulation returns onepromise
, so the return value is{ value:promsie,done:true/false }
- so
co
The recursive processing of the function isres.value.then(...)
And passed in next timeg.next()
Until thedone
为true
It’s over. Does it feel like deja vu? Foo for Generator functions is basically the same as excutorAsync for async await functions, except that * is replaced with async, yield with await, This is why async await is the syntactic sugar of the Generator. This has the advantage of not calling the next method of the traversal object itself and is more semantic.
End, Js asynchronous solution is a front-end player must have the core knowledge, you have mastered?
Small encouragement, great growth, welcome to praise