The meaning and usage of async function
This article is the last article in the series of “In-depth mastery of ECMAScript 6 asynchronous programming”.
- The meaning and usage of Generator function
- The meaning and usage of Thunk function
- The meaning and usage of co function library
- The meaning and usage of async function
1. The ultimate solution
Asynchronous operation is a troublesome thing in JavaScript programming, and it has been so troublesome that various solutions have been put forward to try to solve this problem.
From the earliest callback function, to the Promise object, to the Generator function, there are improvements every time, but it feels incomplete. They all have additional complexity and need to understand the abstract underlying operating mechanism.
Isn’t asynchronous I/O just reading a file, why is it so complicated? The highest level of asynchronous programming is that you don’t need to care about whether it is asynchronous at all.
The async function is the light at the end of the tunnel, and many people think it is the ultimate solution for asynchronous operations.
2. What is the async function?
In a word, the async function is syntactic sugar for the Generator function.
There is a Generator function in the previous article, which reads two files in sequence.
var fs = require('fs'); var readFile = function (fileName){ return new Promise(function (resolve, reject){ fs.readFile(fileName, function(error, data){ if (error) reject(error); resolve(data); }); }); }; var gen = function* (){ var f1 = yield readFile('/etc/fstab'); var f2 = yield readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); };
Written as an async function, it is as follows.
var asyncReadFile = async function (){ var f1 = await readFile('/etc/fstab'); var f2 = await readFile('/etc/shells'); console.log(f1.toString()); console.log(f2.toString()); };
A comparison will reveal that the async function replaces the asterisk (*) of the Generator function with async, and replaces yield with await, and nothing more.
Three, the advantages of async function
The improvement of the Generator function by the async function is reflected in the following three points.
(1) Built-in actuator. The execution of the Generator function must rely on the executor, so there is the co function library, and the async function has its own executor. In other words, the execution of the async function is exactly the same as a normal function, only one line.
var result = asyncReadFile();
(2) Better semantics. Async and await have clearer semantics than asterisk and yield. async indicates that there is an asynchronous operation in the function, and await indicates that the following expression needs to wait for the result.
(3) Broader applicability. The co function library agrees that the yield command can only be followed by the Thunk function or the Promise object, and the await command of the async function can be followed by the Promise object and the value of the primitive type (numerical value, string and Boolean value, but this is equivalent to a synchronous operation ).
Fourth, the realization of async function
The realization of the async function is to wrap the Generator function and the automatic executor in one function.
async function fn(args){ // ... } // 等同于 function fn(args){ return spawn(function*() { // ... }); }
All async functions can be written in the second form above, among which the spawn function is the automatic executor.
The implementation of the spawn function is given below, which is basically a copy of the previous automatic executor.
function spawn(genF) { return new Promise(function(resolve, reject) { var gen = genF(); function step(nextF) { try { var next = nextF(); } catch(e) { return reject(e); } if(next.done) { return resolve(next.value); } Promise.resolve(next.value).then(function(v) { step(function() { return gen.next(v); }); }, function(e) { step(function() { return gen.throw(e); }); }); } step(function() { return gen.next(undefined); }); }); }
The async function is a very new grammatical function. The new arrivals do not belong to ES6, but belong to ES7. At present, it is still in the proposal stage, but the transcoders Babel and regenerator are already supported and can be used after transcoding.
Five, the usage of async function
Like the Generator function, the async function returns a Promise object, and you can use the then method to add a callback function. When the function is executed, once it encounters await, it will return first, wait until the triggered asynchronous operation is completed, and then execute the following statements in the function body.
Below is an example.
async function getStockPriceByName(name) { var symbol = await getStockSymbol(name); var stockPrice = await getStockPrice(symbol); return stockPrice; } getStockPriceByName('goog').then(function (result){ console.log(result); });
The above code is a function to get stock quotes. The async keyword in front of the function indicates that there is an asynchronous operation inside the function. When this function is called, a Promise object will be returned immediately.
The following example specifies how many milliseconds to output a value.
function timeout(ms) { return new Promise((resolve) => { setTimeout(resolve, ms); }); } async function asyncPrint(value, ms) { await timeout(ms); console.log(value) } asyncPrint('hello world', 50);
The above code specifies that after 50 milliseconds, “hello world” is output.
Six, pay attention
The Promise object after the await command may be rejected, so it is best to put the await command in the try…catch code block.
async function myFunction() { try { await somethingThatReturnsAPromise(); } catch (err) { console.log(err); } } // 另一种写法 async function myFunction() { await somethingThatReturnsAPromise().catch(function (err){ console.log(err); }); }
The await command can only be used in async functions. If it is used in ordinary functions, an error will be reported.
async function dbFuc(db) { let docs = [{}, {}, {}]; // 报错 docs.forEach(function (doc) { await db.post(doc); }); }
The above code will report an error because await is used in ordinary functions. However, if the parameter of the forEach method is changed to an async function, there are also problems.
async function dbFuc(db) { let docs = [{}, {}, {}]; // 可能得到错误结果 docs.forEach(async function (doc) { await db.post(doc); }); }
The above code may not work normally, because the three db.post operations will be executed concurrently, that is, executed simultaneously, rather than secondary execution. The correct way of writing is to use a for loop.
async function dbFuc(db) { let docs = [{}, {}, {}]; for (let doc of docs) { await db.post(doc); } }
If you really want multiple requests to be executed concurrently, you can use the Promise.all method.
async function dbFuc(db) { let docs = [{}, {}, {}]; let promises = docs.map((doc) => db.post(doc)); let results = await Promise.all(promises); console.log(results); } // 或者使用下面的写法 async function dbFuc(db) { let docs = [{}, {}, {}]; let promises = docs.map((doc) => db.post(doc)); let results = []; for (let promise of promises) { results.push(await promise); } console.log(results); }
(over)