
Asynchronous is to increase the CPU occupancy rate and keep it busy all the time.
Some operations (the most typical one is I/O) do not require CPU participation and are very time-consuming. If asynchronous is not used, it will form a blocking state, causing the CPU to idle and the page to freeze.
When an I/O operation occurs in an asynchronous environment, the CPU puts the I/O work aside (at this time, the I/O is taken over by other controllers and data is still being transmitted), and then processes the next task, waiting for the I/O operation to be completed. Notify the CPU (callback is a notification method) to come back to work.
The core content of "JavaScript Asynchronous and Callback" is that the specific end time of asynchronous work is uncertain. In order to accurately perform subsequent processing after the asynchronous work is completed, a callback needs to be passed into the asynchronous function, so that After completing your work continue with the following tasks.
Although callbacks can be very simple to implement asynchronously, they can form callback hell due to multiple nesting. To avoid callback hell, you need to denest and change nested programming to linear programming.
Promise is the best solution to handle callback hell in JavaScript .
Promise can be translated as "promise". We can encapsulate asynchronous work and call it a Promise , that is, make a promise and promise to give a clear signal after the asynchronous work ends!
Promise syntax:
let promise = new Promise(function(resolve,reject){
// Asynchronous work}) Through the above syntax, we can encapsulate asynchronous work into a Promise . The function passed in when creating Promise is the method for handling asynchronous work, also known as executor executor.
resolve and reject are callback functions provided by JavaScript itself. They can be called when executor completes the task:
resolve(result) - if it is completed successfully, result will be returned;reject(error) - if the execution fails and error will be generated;executor will automatically execute immediately after Promise is created, and its execution status will change the state of the internal properties of Promise :
state - initially pending , then converted to fulfilled after resolve is called, or becomes rejected when reject is called;result ——It undefined initially, and then becomes value after resolve(value) is called, or becomes error after reject is called;fs.readFile an asynchronous function. We can pass it in executor File reading operations are performed in the file, thereby encapsulating asynchronous work.
The following code encapsulates the fs.readFile function, and uses resolve(data) to handle successful results and reject(err) to handle failed results.
The code is as follows:
let promise = new Promise((resolve, reject) => {
fs.readFile('1.txt', (err, data) => {
console.log('Read 1.txt')
if (err) reject(err)
resolve(data)
})}) If we execute this code, the words "Read 1.txt" will be output, proving that the file reading operation is performed immediately after Promise is created.
Promiseusually encapsulates asynchronous code internally, but it does not only encapsulate asynchronous code.
The above Promise case encapsulates the file reading operation. The file will be read immediately after the creation is completed. If you want to get the result of Promise execution, you need to use three methods then , catch and finally .
The then method of Promise can be used to handle the work after Promise execution is completed. It receives two callback parameters. The syntax is as follows:
promise.then(function(result),function(error))
result is the value received by resolve ;error is the parameter received by reject ;for example:
let promise = new Promise((resolve, reject) => {
fs.readFile('1.txt', (err, data) => {
console.log('Read 1.txt')
if (err) reject(err)
resolve(data)
})})promise.then(
(data) => {
console.log('Successfully executed, the result is' + data.toString())
},
(err) => {
console.log('Execution failed, error is' + err.message)
}) If the file reading is executed successfully, the first function will be called:
PS E:CodeNodedemos 3-callback> node .index.js Read 1.txt If executed successfully, the result is 1
deleted 1.txt . If the execution fails, the second function will be called:
PS E:CodeNodedemos 3-callback> node .index.js Read 1.txt The execution failed with the error ENOENT: no such file or directory, open 'E:CodeNodedemos 3-callback1.txt'
If we only focus on the result of successful execution, we can pass in only one callback function:
promise .then((data)=>{
console.log('Successfully executed, the result is' + data.toString())}) At this point we have implemented an asynchronous reading operation of the file.
If we only focus on the failure result, we can pass null to the first then callback: promise.then(null,(err)=>{...}) .
Or use a more elegant way: promise.catch((err)=>{...})
let promise = new Promise((resolve, reject) => {
fs.readFile('1.txt', (err, data) => {
console.log('Read 1.txt')
if (err) reject(err)
resolve(data)
})})promise.catch((err)=>{
console.log(err.message)}) .catch((err)=>{...}) and then(null,(err)=>{...}) have exactly the same effect.
.finally is a function that will be executed regardless of the result of promise . It has the same purpose as finally in try...catch... syntax, and can handle operations unrelated to the result.
For example:
new Promise((resolve,reject)=>{
//something...}).finally(()=>{console.log('Execute regardless of the result')}).then(result=>{...}, err=>{...} ) The finally callback has no parameters, fs.readFile()
finallyfinally the result of promise will be passed, so it can still be done after finally .thenfs.readFile() method reads 10 files sequentially and outputs the contents of the ten files sequentially.
Since fs.readFile() itself is asynchronous, we must use callback nesting. The code is as follows:
fs.readFile('1.txt', (err, data) => {
console.log(data.toString()) //1
fs.readFile('2.txt', (err, data) => {
console.log(data.toString())
fs.readFile('3.txt', (err, data) => {
console.log(data.toString())
fs.readFile('4.txt', (err, data) => {
console.log(data.toString())
fs.readFile('5.txt', (err, data) => {
console.log(data.toString())
fs.readFile('6.txt', (err, data) => {
console.log(data.toString())
fs.readFile('7.txt', (err, data) => {
console.log(data.toString())
fs.readFile('8.txt', (err, data) => {
console.log(data.toString())
fs.readFile('9.txt', (err, data) => {
console.log(data.toString())
fs.readFile('10.txt', (err, data) => {
console.log(data.toString())
// ==> Hell's Gate})
})
})
})
})
})
})
})
})}) Although the above code can complete the task, as the call nesting increases, the code level becomes deeper and the maintenance difficulty increases, especially when we are using real code that may contain many loops and conditional statements. , instead of the simple console.log(...) in the example.
If we do not use callbacks and directly call fs.readFile() in sequence according to the following code, what will happen?
//Note: This is the wrong way to write fs.readFile('1.txt', (err, data) => {
console.log(data.toString())})fs.readFile('2.txt', (err, data) => {
console.log(data.toString())})fs.readFile('3.txt', (err, data) => {
console.log(data.toString())})fs.readFile('4.txt', (err, data) => {
console.log(data.toString())})fs.readFile('5.txt', (err, data) => {
console.log(data.toString())})fs.readFile('6.txt', (err, data) => {
console.log(data.toString())})fs.readFile('7.txt', (err, data) => {
console.log(data.toString())})fs.readFile('8.txt', (err, data) => {
console.log(data.toString())})fs.readFile('9.txt', (err, data) => {
console.log(data.toString())})fs.readFile('10.txt', (err, data) => {
console.log(data.toString())}) The following are the results of my test (the results of each execution are different):
PS E:CodeNodedemos 3-callback> node .index.The reason
js12346957108
produces this non-sequential result is asynchronous , not multi-threaded parallelism. Asynchronous can be achieved in a single thread.
The reason why this error case is used here is to emphasize the concept of asynchronousness. If you don’t understand why this result occurs, you must go back and make up for the lesson!
The idea of using Promise to solve asynchronous sequential file reading:
promise1 , and use resolve to return the result.promise1.then to receive and output the file reading result.promise2 object in promise1.then . And returnpromise2.then to receive and output the reading resultpromise3 object in promise2.then , and return topromise3.then to receive and output the reading resultThe code is as follows:
let promise1 = new Promise( (resolve, reject) => {
fs.readFile('1.txt', (err, data) => {
if (err) reject(err)
resolve(data)
})})let promise2 = promise1.then(
data => {
console.log(data.toString())
return new Promise((resolve, reject) => {
fs.readFile('2.txt', (err, data) => {
if (err) reject(err)
resolve(data)
})
})
})let promise3 = promise2.then(
data => {
console.log(data.toString())
return new Promise((resolve, reject) => {
fs.readFile('3.txt', (err, data) => {
if (err) reject(err)
resolve(data)
})
})
})let promise4 = promise3.then(
data => {
console.log(data.toString())
//.....
})... ... In this way we write the original nested callback hell into a linear mode.
But there is still a problem with the code. Although the code has become more beautiful in terms of management, it greatly increases the length of the code.
The above code is too lengthy. We can reduce the amount of code through two steps:
promise , and link the .thencode as follows:
function myReadFile (path) {
return new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) reject(err)
console.log(data.toString())
resolve()
})
})}myReadFile('1.txt')
.then(data => { return myReadFile('2.txt') })
.then(data => { return myReadFile('3.txt') })
.then(data => { return myReadFile('4.txt') })
.then(data => { return myReadFile('5.txt') })
.then(data => { return myReadFile('6.txt') })
.then(data => { return myReadFile('7.txt') })
.then(data => { return myReadFile('8.txt') })
.then(data => { return myReadFile('9.txt') })
.then(data => { return myReadFile('10.txt') }) Since the myReadFile method will return a new Promise , we can directly execute the .then method. This programming method is called chain programming .
The code execution result is as follows:
PS E:CodeNodedemos 3-callback> node .index.js12345678910
This completes the asynchronous and sequential file reading operation.
Note: A new
Promiseobject must be returned in the.thenmethod of each step, otherwise the previous oldPromisewill be received.This is because each
thenmethod will continue to pass itsPromisedownward.