preface
Before reading this article, you need to have some understanding of async-await and generator. If you don’t know, check out the previous article on the use of async-await and generator-iterators
Put forward the demand
There is currently a network request function, and I want to make sure that the parameters passed in the next network request are based on the results of the previous network request. For example, pass ‘aaa’ the first time, then add ‘BBB’ to the result of the first request the second time, and so on.
function request(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = >{ // Use timer to simulate network request
resolve(url)
}, 1000)})}Copy the code
The solution
Promise to realize
request('aaa').then(res= > {
// where res is 'aaa'
request(res + 'bbb').then(res= > {
// where res is 'aaabbb'
request(res + 'ccc').then(res= > {
console.log(res) // 'aaabbbccc'})})})Copy the code
- As you can see, the Promise. Then method above can be used to practice the desired effect, and continue the request in then to achieve the desired effect. However, the code above also has multiple layers of THEN nesting, and currently only sends three network requests. If it sends 30, this code will create callback hell.
Async and await
async function foo() {
const result1 = await request('aaa') // result1 results in 'aaa'
const result2 = await request(result1 + 'bbb') // result2 results in 'aaabbb'
const result3 = await request(result2 + 'ccc')
console.log(result3) // 'aaabbbccc'
}
foo()
Copy the code
- We can see that using async and await can also achieve the desired effect, and using this method allows us to write code using synchronous methods. This is very friendly for us to read the code.
- This is also the recommended solution to this requirement.
Implementation using generator
// Define the generator function
function* generator() {
const result1 = yield request('aaa') // The first call to next stops here
const result2 = yield request(result1 + 'bbb') // The second call to next will stop here
const result3 = yield request(result2 + 'ccc') // A third call to next will stop here
// Call next for the fourth time to execute all the following code
console.log(result3) / / print 'aaabbbccc'
}
const gene = generator() // Call the generator function to generate the generator
// Calling next returns an iterator whose value is the Promise returned by Request ('aaa')
gene.next().value.then(res1= > {
// res1 results in 'aaa'
// Call next and pass in res1, which is passed to result1 in the generator function
// The next function returns an iterator whose value is the Promise returned by Request (result1 + 'BBB ')
gene.next(res1).value.then(res2= > {
// res2 results in 'aaabbb'
// Call the next function and pass in res2, which is passed to the result2 in the generator function
// The next function returns an iterator whose value is the Promise returned by Request (result2 + 'CCC ')
gene.next(res2).value.then(res3= > {
// res3 results in 'aaabbbCCC'
// Call next and pass res3, which is passed to result3 in the generator function
gene.next(res3)
})
})
})
Copy the code
- The code above might feel a little hard to see, and it looks worse than the callback implemented with Promise.
- However, if we look at generator functions alone, we will see that writing in generator functions is basically the same as writing with async-await except for the different keywords.
- Looking at the next function below, we can see that there is a pattern, so we can use a recursive function to help us implement this process without having to write it manually.
Implements a function that automatically performs the next operation
function execuGene(generatorFn) {
// Execute the passed generator function to get the generator
const gene = generatorFn()
// Define a recursive function
function recurse(url) {
// Call next and pass the URL argument to get the iterator
const result = gene.next(url)
// Determine the done state of the iterator
if(! result.done) {// Enter here, indicating that the generator is not finished
result.value.then(res= >{
// Call recurse recursively
recurse(res)
})
}
}
recurse()
}
function* generator() {
const result1 = yield request('aaa') // The first call to next stops here
const result2 = yield request(result1 + 'bbb') // The second call to next will stop here
const result3 = yield request(result2 + 'ccc') // A third call to next will stop here
// Call next for the fourth time to execute all the following code
console.log(result3) / / print 'aaabbbccc'
}
// Now you just need to change the next code to this one line to achieve the same effect
execuGene(generator)
Copy the code
- You can see that after one handler, the code we are writing is basically the same as the async-await implementation. This is how async-await is implemented.
conclusion
Async-await is essentially syntactic sugar for generator and Promise. The precise control of the function is realized through the yield keyword of generator function, and asynchronous operation is realized through synchronous code. We can also use a recursive function to help with the tedious operation of the next call to the generator.