Asynchronous programming is a problem that every JavaScript programmer will encounter, whether it's a front-end ajax request or a node's various asynchronous API s. This article will summarize four common methods to deal with asynchronous programming.
callback
The use of callback functions is the most common form. Here are a few examples:
// jQuery ajax $.get('test.html', data => { $('#result').html(data) })
// node reads files asynchronously const fs = require('fs') fs.readFile('/etc/passwd', (err, data) => { if (err) { throw err } console.log(data) })
Callback function is very easy to understand, that is, when defining a function, another function (callback function) is passed into the defined function as a parameter. When the asynchronous operation is completed, the callback function is executed, so as to ensure that the next operation is executed after the asynchronous operation.
The disadvantage of callback function is that when many asynchronous operations need to be performed, many callback functions will be nested together and the code structure is chaotic, which is called callback hell.
func1(data0, data1 => { func2(data2, data3 => { func3(data3, data4 => data4) }) })
Promise
Promise uses a chain call method to organize asynchronous code, which can change the code originally called in the form of callback function to chain call.
// jQuery ajax promise mode $.get('test.html') .then(data => $(data)) .then($data => $data.find('#link').val('href')) .then(href => console.log(href))
Defining a function in Promise form is also very simple in ES6.
function ready() { return new Promise((resolve, reject) => { setTimeout(() => { resolve('ready') }, 3000) }) } ready().then(ready => console.log(`${ready} go!`))
In versions above node 8.0, util.promisify method can also be used to convert callback functions into Promise forms.
const util = require('util') const fs = require('fs') const readPromise = util.promisify(fs.readFile) readPromise('test.txt').then(data => console.log(data.toString()))
To get a better understanding of Promise, you can read my poems. On Promise Object of ES6.
Generators
TJ, the famous developer of node, developed an asynchronous control tool using ES6 Generators. co.
If you don't know Generators, you can read the following articles:
Using co, asynchronous code can be written in a form similar to synchronous code:
const util = require('util') const fs = require('fs') const co = require('co') const readFile = util.promisify(fs.readFile) co(function* () { const txt = yield readFile('file1.txt', 'utf8') console.log(txt) const txt2 = yield readFile('file2.txt', 'utf8') console.log(txt2) })
It seems obvious that using Generators can make asynchronous code very clear, but the disadvantage is to introduce additional libraries to take advantage of this feature.
Async/Await
Versions above node7.6 introduce a new feature of ES7, Async/Await, which is specifically designed to control asynchronous code. Let's start with an example:
const util = require('util') const fs = require('fs') const readFile = util.promisify(fs.readFile) async function readFiles () { const txt = await readFile('file1.txt', 'utf8') console.log(txt) const txt2 = await readFile('file2.txt', 'utf8') console.log(txt2) })
First, we need to use the async keyword to define a function that contains asynchronous code. We can write asynchronous into synchronous operation by using await keyword before the asynchronous function in Promise form.
It seems to be similar to Generators control, but Async/Await is native to control asynchrony, so it is recommended.
error handling
Finally, the error handling of the following four asynchronous control methods is discussed.
callback
The error handling of callback function is very simple. It is to return error information in callback function at the same time.
const fs = require('fs') fs.readFile('file.txt', (err, data) => { if (err) { throw err } console.log(data) })
Promise
Promise uses a catch scheme after the then method to capture error messages:
const fs = require('fs') const readFile = util.promisify(fs.readFile) readFile('file.txt') .then(data => console.log(data)) .catch(err => console.log(err))
Generators and sync/Await
Generators are similar to Async/Await in two ways: the first uses Promise's catch method and the second uses try catch keyword.
Promise catch
const fs = require('fs') const co = require('co') const readFile = util.promisify(fs.readFile) co(function* () { const data = yield readFile('file.txt').catch(err => console.log(err)) })
const fs = require('fs') const co = require('co') const readFile = util.promisify(fs.readFile) async function testRead() { const data = await readFile('file.txt').catch(err => console.log(err)) }
try/catch
const fs = require('fs') const co = require('co') const readFile = util.promisify(fs.readFile) co(function* () { try { const data = yield readFile('file.txt') } catch(err) { console.log(err) } })
const fs = require('fs') const readFile = util.promisify(fs.readFile) async function testRead() { try { const data = await readFile('file.txt') } catch(err) { console.log(data) } }
Thank you for your reading. Please point out the shortcomings for me.
Reference resources