10. Async异步函数
NOxONE 2/20/2022 ES6
# 1. Async 函数介绍
asnyc 函数实际上是Generator
+Promise
的语法糖
const fs = require('fs')
function readFile(fileName) {
return new Promise(function (resolve, reject) {
fs.readFile(fileName, function (error, data) {
// 异步
if (error) reject(error)
else resolve(data)
})
})
}
// then调用
readFile('/etc/fstab').then((f1) => {
readFile('/etc/shells').then((f2) => {
console.log(f1, f2)
})
})
// genarator调用
function* gen() {
const f1 = yield readFile('/etc/fstab')
const f2 = yield readFile('/etc/shells')
console.log(f1.toString(), f2.toString())
}
let f = gen()
f.next() // 得到f1
f.next() // 得到f2
f.next() // 输出结果
上述代码修改成 async 函数
async function asyncReadFile() {
const f1 = await readFile('/etc/fstab') // 得到f1
const f2 = await readFile('/etc/shells') // 得到f2
console.log(f1.toString(), f2.toString()) // 输出结果
}
asyncReadFile()
# 2. Async 函数基本用法
async 函数返回一个Promise
对象,可以使用 then 方法进行回调。当函数执行遇到 await 就会先暂时,等异步操作完成后,在接着执行函数体后面的语句
async function getStockPriceByName(name) {
const symbol = await getStockSymbol(name)
const stockPrice = await getStockPrice(symbol)
return stockPrice
}
getStockPriceByName('goog').then(function (result) {
console.log(result)
})
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms) // ms秒之后resolve
})
}
async function asyncPrint(value, ms) {
await timeout(ms)
console.log(value)
}
asyncPrint('hello world', 50) // 执行异步操作,在50ms后才会输出hello world
console.log('哈哈') // 先打印
# 3. async 声明方式
// 函数声明
async function fn() {}
// 函数表达式
const fn = async function () {}
// 箭头函数
const fn = async () => {}
// 对象方法
let obj = { async fn() {} }
// Class的方法
class Storage {
constructor() { ... }
async fn() { ... }
}
# 4. async 本质上是 Promise
async 函数结束 return 的参数会封装成为一个Promise
,状态为fulfilled
,因为函数本身就是一个Promise
async function fn() {
return 'data'
}
fn().then((res) => console.log(res)) // data
同理,throw 抛出错误会返回一个Promise
,状态为rejected
async function fn(){
throw new Error('出错了')
}
fn().then{
res => console.log('resolve: ' + res),
err => console.log('reject: ' + err)
}
// reject Error: 出错了
返回的 Promise 对象,必须等到 await 命令后面的 Promise 对象执行完,才会发生状态改变,意味着必须等到 async 函数异步操作执行完才会执行 then(看有关 Generator 函数和 Promise 结合模拟实现 async/await (opens new window)会更清楚这个过程)
async function getTitle(url) {
let res = await fetch(url)
let html = await res.text()
return html.match(/<title>([\s\S]+)<\/title>/i)[1]
}
getTitle('https://www.baidu.com').then((res) => console.log(res))
// 抓取网页、取出文本、匹配页面标题。只有这三个操作全部完成,才会执行then方法里面的console.log
# 5. await 命令后面若是thenable
对象,会将其视为 Promise 处理,调用其 then 方法并接收其 resolve 函数的参数
class Sleep {
constructor(timeout) {
this.timeout = timeout
}
then(resolve, reject) {
const startTime = +new Date()
setTimeout(() => resolve(+new Date() - startTime), this.timeout)
}
}
;(async () => {
const sleepTime = await new Sleep(3000)
console.log(sleepTime)
})()
// 3000
# 6. await 命令后面的 Promise 对象处于 reject 状态时,会立即中断执行,并抛错传入 catch 回调函数
async function f() {
await Promise.reject('出错了')
await Promise.resolve('hello world') // 不会执行
}
f()
.then((v) => console.log(v)) // 不执行
.catch((e) => console.log(e)) // 出错了
若不希望中断,那么就要把其放入try...catch
里
async function f() {
try {
await Promise.reject('出错了')
} catch (e) {
console.log(e) // 出错了
}
await Promise.resolve('hello world') // 继续执行
}
f().then((v) => console.log(v)) // hello world
或者及时在 await 后面的 Promise 对象调用 catch 方法处理错误
async function f() {
await Promise.reject('出错了').catch((e) => console.log(e))
return await Promise.resolve('hello world') // 继续执行
}
f().then((v) => console.log(v))
// 出错了
// hello world
# 7. 良好实践
多次尝试获取数据
const superagent = require('superagent')
const NUM_RETRIES = 3
async function test() {
let i
for (i = 0; i < NUM_RETRIES; ++i) {
try {
await superagent.get('http://google.com/this-throws-an-error')
break
} catch (err) {}
}
console.log(i)
}
test() // 3
// 证明请求都是无效的,因为只要成功,就会立刻break跳出循环
将继发改为并行触发(很重要)
async function fn() {
let foo = await getFoo()
let bar = await getBar()
// 这里bar要等foo获得数据后才会执行,是继发的关系,
// 但是,这样很费事,两个应该是并行触发的才对
// 改进方法
let [foo, bar] = await Promise.all([getFoo(), getBar()])
// 或者
let fooPromise = getFoo()
let barPromise = getBar()
let foo = await fooPromise
let bar = await barPromise
}