章
目
录
JavaScript异步编程中我们已经有了promise
来处理异步操作,可为何官方还要引入async
和await
呢?这背后其实有着深层次的考量,接下来就让我们一探究竟。
一、异步编程中的难题与现有方案
在实际开发中,发接口请求是常见的操作。有时,我们需要保证接口请求的顺序,然而各个接口的耗时又不尽相同,这就需要将异步操作“捋”成同步的效果。
(一)promise解决异步的方式及问题
以模拟接口请求为例,下面这段代码展示了使用promise
解决异步的方式:
function request (num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * 10)
}, 1000)
})
}
// const res1 = request(1)
// const res2 = request(res1)
request(1).then(res1 => {
console.log(res1);
request(res1).then(res2 => {
console.log(res2);
})
})
上述代码实现了两次接口请求,且第二次请求依赖第一次请求返回的结果。但这种链式调用的方式,代码看起来较为繁琐,不够优雅。
(二)generator处理异步的情况
再来看generator
处理异步的方式,以下是模拟三次请求的代码:
function* gen() { // generator处理异步
const num1 = yield request(1)
const num2 = yield request(num1)
const num3 = yield request(num2)
return num3
}
let g = gen()
const next1 = g.next()
next1.value.then(res1 => {
console.log(res1);
const next2 = g.next(res1)
next2.value.then(res2 => {
console.log(res2);
const next3 = g.next(res2)
next3.value.then(res3 => {
console.log(res3);
})
})
})
可以看到,generator
处理异步时,代码同样不美观,调用过程也比较复杂。
二、async和await的优势
async
和await
的出现,很好地解决了上述问题。下面通过代码来看看它们是如何工作的:
async function fn() {
const res1 = await request(1) // await会把promise中的resolve值提取出来
const res2 = await request(res1)
console.log(res2);
}
fn()
相较于promise
的链式调用和generator
复杂的调用方式,async
和await
的代码更加简洁直观,将异步操作写得如同同步操作一般,极大地提高了代码的可读性。
三、async的实现原理
async
和await
的实现是基于promise
和generator
的。其中,async
关键字会让函数默认返回一个promise
对象。如果一个函数接收的参数是函数体,或者返回一个函数体,那么这个函数就是高阶函数
,async
相关的函数就属于这类。
async
的核心其实是generator
,只不过在使用generator
时,需要手动不断调用next
函数,而async
通过递归实现了generator
的next
函数自动化执行。以下是async
的核心实现代码(类似co
模块的源码loop
函数):
function generatorToAsync(generatorFn) {
// 生成generator对象
const gen = generatorFn()
return function () {
return new Promise((resolve, reject) => {
function loop (key, arg) {
let res = null
// 执行generator的next方法或throw方法
res = gen[key](arg)
const { value, done } = res
// 如果generator执行结束
if (done) {
return resolve(value)
} else {
// 将value转为promise,处理异步结果
Promise.resolve(value).then(res => {
loop('next', value)
})
}
}
// 开始执行
loop('next')
})
}
}
在这段代码中,generatorToAsync
函数接收一个generatorFn
,返回一个新函数。新函数返回一个Promise
,内部通过loop
函数递归调用generator
的next
方法,直到done
为true
,结束递归并返回最终结果。
四、async和await的不足
async
和await
虽然强大,但也并非完美。它没有内置的错误捕获机制,所以在使用时,需要开发者手动使用try...catch
来捕获可能出现的错误。
async
和await
通过递归自动化执行generator
的next
函数,优化了异步操作的代码编写方式。