How asynchronous requests are handled
Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.
In daily development, we often need to make an asynchronous request, get the result of the asynchronous request, and then go to another interface to find the corresponding value through the result
/ / requirements:
// 1> url: exmaple -> res: example
// 2> url: res + "aaa" -> res: example-aaa
// 3> url: res + "bbb" => res: exmaple-aaa-bbb
Copy the code
Many times the callback
// Use timer to simulate request
function request(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url) {
resolve(url)
} else {
reject('url is not defined')}},1000)
})
}
request('example').then(res= > {
request(`${res}-aaa`).then(res= > {
request(`${res}-bbb`).then(res= > {
console.log(res)
})
})
})
Copy the code
This solution is the simplest and most straightforward
But if we need too many asynchronous requests, it’s easy to see the callback hell shown above
This is very detrimental to maintenance and extension later
The return value for then in the Promise
// Use timer to simulate request
function request(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url) {
resolve(url)
} else {
reject('url is not defined')}},1000)})}// This workaround is to change the nesting of callback functions to same-level calls
// But it still does not fundamentally solve the maintenance and extension problems caused by multiple callbacks
request('example').then(res= > request(`${res}-aaa`))
.then(res= > request(`${res}-bbb`))
.then(res= > console.log(res))
Copy the code
Promise + generator
The solution is to write asynchronous code in a synchronous manner
This is the best solution until async + await comes along
// Use timer to simulate request
function request(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url) {
resolve(url)
} else {
reject('url is not defined')}},1000)})}function* fetchData() {
let res = yield request('example')
res = yield request(`${res}-aaa`)
res = yield request(`${res}-bbb`)
console.log(res)
}
// Execute the generator function
function execGenerator(generatorFn) {
const generator = generatorFn()
function exec(result) {
const { done, value: promise } = generator.next(result)
if (done) {
return result
}
promise.then(res= > {
exec(res)
})
}
exec()
}
execGenerator(fetchData)
Copy the code
In practice, there is a third-party library, CO, that can actively help us execute generator functions without having to write the corresponding execGenerator manually
#The installation
$ npm i co
Copy the code
// Use timer to simulate request
function request(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url) {
resolve(url)
} else {
reject('url is not defined')}},1000)})}function* fetchData() {
let res = yield request('example')
res = yield request(`${res}-aaa`)
res = yield request(`${res}-bbb`)
console.log(res)
}
const co = require('co')
co(fetchData)
Copy the code
async + await
Async +await is the keyword introduced in ES8. It is the actual way to handle requests asynchronously in the current development
It can be assumed that async+await will still be converted to Promise+Generator when parsing
Async +await is just a syntactic sugar form of Promise+Generator
function request(url) {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
if (url) {
resolve(url)
} else {
reject('url is not defined')}},1000)})}async function fetchData() {
let res = await request('example')
res = await request(`${res}-aaa`)
res = await request(`${res}-bbb`)
console.log(res)
}
fetchData()
Copy the code
Basic definition of async functions
The async keyword is used to declare an asynchronous function
async function foo() {}
const bar = async() = > {}class Person {
async baz(){}}Copy the code
Asynchronous functions execute the process
Asynchronous functions perform the same internal code execution as normal functions and are executed synchronously by default
async function foo() {
console.log('Script in foo')}console.log('script started')
foo()
console.log('script endedd')
/* => script started foo script endedd */
Copy the code
The difference between an asynchronous function and a normal function
Difference 1: Asynchronous functions can also return values, but the return value of asynchronous functions is wrapped in promise.resolve
// do not write the declaration even in asynchronous functions
// The default return value is also a promise, but promise. Resolve (undefined)
async function foo() {}const promise = foo()
promise.then(res= > console.log('Asynchronous function return value:', res))
Copy the code
async function foo() {
return 123
}
const promise = foo()
promise.then(res= > console.log(res)) / / = > 123
Copy the code
async function foo() {
// If the asynchronous function returns a thenable object
// Then the returned Thenable object is automatically executed to get the final value
return {
then(resolve, reject) {
resolve(123)}}}const promise = foo()
promise.then(res= > console.log(res)) / / = > 123
Copy the code
async function foo() {
return new Promise(resolve= > resolve(123))}const promise = foo()
promise.then(res= > console.log(res)) / / = > 123
Copy the code
When an asynchronous function throws an exception, it does not report an error like a normal function, but passes it as a Reject, a Promise
async function foo() {
throw new Error('something error in async function')}const promise = foo()
promise.catch(e= > console.log(e))
Copy the code
Difference 3: You can use the await keyword inside asynchronous functions, but not inside normal functions
function request() {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(123)},1000)})}async function foo() {
// await is followed by an expression that returns a Promise
// When the state of promise returned behind await changes to Resolved.
// The result is assigned to res as the value of the return value of the await expression
const res = await request()
// The logic behind an await expression waits for the asynchronous code corresponding to an await expression to complete execution
// We can think of the logic behind the await expression as the logic that needs to be written in the then method of the promise returned by the await expression
console.log(res)
}
foo()
Copy the code
// If await is followed by an ordinary value, the value will be returned directly
function request() {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(123)},1000)})}async function foo() {
// If "await" is followed by a basic datatype
Resolve (123)
const res = await 123
console.log(res) / / = > 123
}
foo()
Copy the code
If the expression following await returns a Promise that is a resolve state, the reject result is used directly as the resolve value of the function’s Promise.
function request() {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(123)},1000)})}async function foo() {
const res = await {
then(resolve) {
resolve(123)}}console.log(res) / / = > 123
}
foo()
Copy the code
function request() {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(123)},1000)})}async function foo() {
const res = await new Promise(resolve= > resolve(123))
console.log(res) / / = > 123
}
foo()
Copy the code
If the expression following await returns a reject Promise, the reject result will be used as the reject value of the function’s Promise.
function request() {
return new Promise((resolve, reject) = > {
setTimeout(() = > {
reject(123)},1000)})}async function foo() {
try {
const res = await request()
console.log(res)
} catch(err) {
console.log('err', err)
}
}
foo()
Copy the code