10. Async异步函数

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
}
    人生如梦,
    我投入了的却是真情,
    世界先爱了我,
    我不能不爱它。
    红莲华
    x
    loading...