ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## async函数 ### 含义 ES2017标准引入了 `async` 函数,用一句话解释,它就是 `Generator` 函数的语法糖。 先来看看 `Generator` 函数如何依次读取两个文件 ```js let fs = require('fs') let readFile = function (fileName) { return new Promise(function (resolve, reject) { fs.readFile(filename, function (error, data) { if (error) return reject(error) resolve(data) }) }) } let gen = function* () { let f1 = yield readFile('/etc/fstab') let f2 = yield readFile('/etc/shells') console.log(f1.toString()) console.log(f2.toString()) } ``` 将上面的代码写成 `async` 的形式: ```js let asyncReadFile = async function () { let f1 = await readFile('/etc/fstab') let f2 = await readFile('/etc/shells') console.log(f1.toString()) console.log(f2.toString()) } ``` `async` 函数对 `Generator` 函数的改进体现在以下4点: - 内置执行器:`Generator` 函数的执行必须依靠执行器,所以才有 `co` 模块,而 `async` 函数自带执行器 - 更好的语义:`async` 和 `await` 比星号和 `yield` 更加语义化。 - 更广的适用性 - 返回值是`Promise` ### 用法 `async` 函数返回一个 `Promise` 对象,可以使用 `then` 方法添加回调函数,当函数执行时,一旦遇到 `await` 就会先返回,等到异步操作完成,再接着执行函数体内后面的语句 ```js async function getStockPriceByName (name) { let symbol = await getStockSymbol(name) let stockPrice = await getStockPrice(symbol) return stockPrice } getStockPriceByName('goog').then(function (result) { console.log(result) }) ``` ### 语法 #### 返回Promise对象 `async` 函数返回一个 `Promise` 对象。`async` 函数内部 `return` 语句返回的值,会成为 `then` 方法回调函数的参数 ```js async function f () { return 'hello world' } f().then(v => console.log(v)) // 'hello world' ``` `async` 函数内部抛出的错误会导致返回的 `Promise` 对象变为 `reject` 状态,抛出的错误对象会被 `catch` 方法捕获 ```js async function f () { throw new Error('some is wrong') } f().then( v => console.log(v), e => console.log(e) ) // Error: some is wrong ``` #### Promise对象的状态变化 `async` 函数返回的 `Promise` 对象必须等到内部所有的 `await` 命令后面的 `Promise` 对象执行完才会发生状态变化,除非遇到 `return` 语句或抛出错误。也就是只有 `async` 函数内部的异步操作执行完,才会执行 `then` 方法指定的回调函数。 #### await命令 正常情况下,`await` 命令后面是一个 `Promise` 对象,如果不是,会被转化成一个立即 `resolve` 的 `Promise` 对象 #### 错误处理 如果 `await` 后面的异步操作出错,那么等同于 `async` 函数返回的 `Promise` 对象被 `reject` ```js async function f () { await new Promise (function (resolve, reject) { throw new Error('出错了') }) } f().then(v => console.log(v)).catch(e => console.log(e)) // Error:出错了 ``` 防止这种情况的方法就是将其放在 `try...catch`当中 ```js async function f () { try { await new Promise(function (resolve, reject) { throw new Error('出错了') }) } catch (e) {} return await('hello world') } ``` #### 注意点 1. `await` 命令后面的 `Promise` 对象的运行结果可能是 `rejected`,所以最好把 `await` 命令放在 `try...catch`当中 ```js async function myFunction () { try { await somethingThatReturnAPromise() } catch (err) { console.log(err) } } ``` 2. 多个 `await` 命令后面的异步操作如果不存在继发关系,最好让他们同时触发 ```js // bad 比较耗时 let foo = await getFoo() let bar = await getBar() // good let [foo, bar] = await Promise.all([getFoo(), getBar()]) ``` 3. `await` 命令只能在 `async` 函数之中,如果用在普通函数中就会报错。 ### async函数的实现原理 `async` 函数的实现原理就是将 `Generator` 函数和自动执行器包装在一个函数里。