async 函数是什么
一句话,async 函数就是 Generator 函数的语法糖。如下代码:
//次读取两个文件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());};//写成 async 函数,就是下面这样。var asyncReadFile = async function (){var f1 = await readFile('/etc/fstab');var f2 = await readFile('/etc/shells');console.log(f1.toString());console.log(f2.toString());};
一比较就会发现,async 函数就是将 Generator 函数的星号()替换成 async,将 yield 替换成 await,仅此而已。
*补充:
co函数库 是一个 generator 函数的自启动执行器,使用条件是 generator 函数的 yield 命令后面,只能是 thunk 函数或 Promise 对象,co 函数执行完返回一个 Promise 对象。async、await 是 co 库的官方实现。也可以看作自带启动器的 generator 函数的语法糖。
async 的用法
同 Generator 函数一样,async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。当函数执行的时候,一旦遇到 await 就会先返回,等到触发的异步操作完成,再接着执行函数体内后面的语句。
async function getStockPriceByName(name) {var symbol = await getStockSymbol(name);var stockPrice = await getStockPrice(symbol);return stockPrice;}getStockPriceByName('goog').then(function (result){console.log(result);});//上面代码是一个获取股票报价的函数,函数前面的async关键字,表明该函数内部有异步操作。调用该函数时,会立即返回一个Promise对象。
下面是一个更加简单的例子:
function timeout(value) {return new Promise((resolve) => {setTimeout(()=>resolve(value), 1000);});}async function asyncPrint(value) {var aa = await timeout(value);console.log(aa)return 'ceshi'}asyncPrint('hello world');//上面代码指定1000毫秒以后,输出"hello world"。//asyncPrint返回的是一个promise,所以可以在asyncPrint函数内返回一个值,然后then中获取到asyncPrint('hello chentao').then(res=>{console.log(res)//ceshi})
async/await 的优势
处理 then 链
继续上面的例子如果直接用promise实现如下:
function timeout(value) {return new Promise((resolve) => {setTimeout(()=>resolve(value), 1000);});}timeout("hello word").then(function(res){console.log(res)})
这里两种方式对异步调用的处理(实际就是对 Promise 对象的处理)差别并不明显,甚至使用 async/await 还需要多写一些代码,那它的优势到底在哪?
单一的 Promise 链并不能发现 async/await 的优势,但是,如果需要处理由多个 Promise 组成的 then 链的时候,优势就能体现出来了(很有意思,Promise 通过 then 链来解决多层回调的问题,现在又用 async/await 来进一步优化它)。
假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。我们仍然用 setTimeout 来模拟异步操作:
/*** 传入参数 n,表示这个函数执行的时间(毫秒)* 执行的结果是 n + 200,这个值将用于下一步骤*/function takeLongTime(n) {return new Promise(resolve => {setTimeout(() => resolve(n + 200), n);});}function step1(n) {console.log(`step1 with ${n}`);return takeLongTime(n);}function step2(n) {console.log(`step2 with ${n}`);return takeLongTime(n);}function step3(n) {console.log(`step3 with ${n}`);return takeLongTime(n);}
现在用 Promise 方式来实现这三个步骤的处理
function doIt() {console.log("doIt");const time1 = 300;step1(time1).then(time2 => step2(time2)).then(time3 => step3(time3)).then(result => {console.log(`result is ${result}`);});}doIt();// doIt// step1 with 300// step2 with 500// step3 with 700// result is 900
如果用 async/await 来实现呢,会是这样
async function doIt() {console.log("doIt");const time1 = 300;const time2 = await step1(time1);const time3 = await step2(time2);const result = await step3(time3);console.log(`result is ${result}`);}doIt();
结果和之前的 Promise 实现是一样的,但是这个代码看起来是不是清晰得多,几乎跟同步代码一样
参数传递
现在把业务要求改一下,仍然是三个步骤,但每一个步骤都需要之前每个步骤的结果。
function step1(n) {console.log(`step1 with ${n}`);return takeLongTime(n);}function step2(m, n) {console.log(`step2 with ${m} and ${n}`);return takeLongTime(m + n);}function step3(k, m, n) {console.log(`step3 with ${k}, ${m} and ${n}`);return takeLongTime(k + m + n);}
先看promise的写法:
function doIt() {console.time("doIt");const time1 = 300;step1(time1).then(time2 => {return step2(time1, time2).then(time3 => [time1, time2, time3]);}).then(times => {const [time1, time2, time3] = times;return step3(time1, time2, time3);}).then(result => {console.log(`result is ${result}`);console.timeEnd("doIt");});}doIt();// step1 with 300// step2 with 800 = 300 + 500// step3 with 1800 = 300 + 500 + 1000// result is 2000
再看async/await
async function doIt() {console.time("doIt");const time1 = 300;const time2 = await step1(time1);const time3 = await step2(time1, time2);const result = await step3(time1, time2, time3);console.log(`result is ${result}`);console.timeEnd("doIt");}doIt();
到这里就发现,相对于promise,async的写法会简单很多。promise想要将前面操作的结果传递给后续的异步太难处理
注意点
await 命令后面的 Promise 对象,运行结果可能是 rejected,所以最好把 await 命令放在 try…catch 代码块中。
async function myFunction() {try {await somethingThatReturnsAPromise();} catch (err) {console.log(err);}}// 另一种写法async function myFunction() {await somethingThatReturnsAPromise().catch(function (err){console.log(err);});}
https://segmentfault.com/a/1190000007535316
http://www.ruanyifeng.com/blog/2015/05/async.html
本文是这两篇文章的学习笔记
