Async-Await
基本介绍
之前解决异步我们一直使用Promise
的.then()
方案,虽然解决了回调地狱的情况,但使用链式写法也并不特别优雅。比如看下面的代码。
所以就出现了一种号称异步的终极方案Async
、Await
。我们看他的定义
async 函数是使用async关键字声明的函数。async 函数是 AsyncFunction 构造函数的实例,并且其中允许使用
await 关键字。async 和 await 关键字让我们可以用一种更简洁的方式写出基于 Promise 的异步行为,而无需刻意地链式调用
promise。
我们可以把代码改写成下面这样:
通过上面的改写,我们可以使用同步的写法来实现异步编程。
需要注意的是,如果Promise
是被reject
的,需要通过try catch
来捕获,代码如下所示:
注意事项
同一个async
函数中的await
前面的await
先执行完成之后再执行后面的await
,但不同async
函数中的await
是不会互相阻塞的。比如常见的就是forEach
,我们看下面代码:
const promise1 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise1 val'), 1000)
})
const promise2 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise2 val'), 1000)
})
const promise3 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise3 val'), 1000)
})
const promiseList = [promise1, promise2, promise3];
// 打印结果不符合预期,三个值在等待1s后同时打印出来了并没有依次执行
promiseList.forEach(async promiseItem => {
const val = await promiseItem()
console.log(val);
})
执行效果如下所示:
执行效果不符合预期的原因就是forEach
中的三个await
在三个不同的async
函数中,是相互独立的,并不会await
我们将代码改成如下方式:
const promise1 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise1 val'), 1000)
})
const promise2 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise2 val'), 1000)
})
const promise3 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise3 val'), 1000)
})
const promiseList = [promise1, promise2, promise3];
// 输出符合预期
// 先打印promise1 val等1s后打印promise2 val再等1s后打印promise3 val
const fn = async () => {
for (let i = 0; i < promiseList.length; i++) {
// for循环的三个await 都在同一个async函数中
// 所以第二个await 会等第一个await,第三个await会等第二个await依次执行
const val = await promiseList[i]()
console.log(val);
}
}
fn()
执行效果如下所示:
打印效果符合预期,因为在这里三个await
是在同一个async
函数中的,所以会依次执行每一个await
Async的实现原理 - Generator
基本介绍
上面提到async 函数是 AsyncFunction 构造函数的实例,async function*
声明定义了一个异步生成器函数,其返回一个 AsyncGenerator 对象。
比如说我们一般在代码中有三个方法分别获取一些后台的数据,如果我们使用async
函数的话,一般这么写:
const getAjaxData1 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise1 val'), 1000)
})
const getAjaxData2 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise2 val'), 2000)
})
const getAjaxData3 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise3 val'), 3000)
})
const promiseFn = async () => {
const val1 = await getAjaxData1()
console.log('接受到的值1', val1)
const val2 = await getAjaxData2()
console.log('接受到的值2', val2)
const val3 = await getAjaxData3()
console.log('接受到的值3', val3)
}
promiseFn()
效果如下所示:
其实async
是会使用function *
生成一个函数异步生成器。如下如所示:而每一个yield
后面的逻辑,都需要生成器函数调用next()
方法执行,其返回值有两部分组成
- done:表当前函数生成器是否执行完,false表未执行完,true表执行完
- value:每个
yield
后面的返回值(在这里就是返回一个promise
对象)
实际上通过function *
获取promise
的值完整逻辑是这样:
const getAjaxData1 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise1 val'), 1000)
})
const getAjaxData2 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise2 val'), 2000)
})
const getAjaxData3 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise3 val'), 3000)
})
// generate执行每一个yield获取数据
function* getAjaxDataStep() {
yield getAjaxData1()
yield getAjaxData2()
yield getAjaxData3()
}
// 先执行 function* 一次,得到迭代器
// promise实例通过.then方法获取promise的结果
const myGenerate = getAjaxDataStep()
myGenerate.next().value.then(val => {
console.log('接受到的值1', val)
return myGenerate.next().value
}).then(val => {
console.log('接受到的值2', val)
return myGenerate.next().value
}).then(val => {
console.log('接受到的值3', val)
return myGenerate.next().value
}).then(val => {
console.log('接受到的值4', val)
return myGenerate.next().value
})
其执行效果如下:
可以看到和上面使用Promise
执行得到的效果一样。
实现一个自动执行的Generator
我们如果不使用上面async await
或者是Promise
对象.then()
的方式,我们也可以封装一个函数,自动执行function *
中的每一个任务,来获取每个值
const getAjaxData1 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise1 val'), 1000)
})
const getAjaxData2 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise2 val'), 2000)
})
const getAjaxData3 = () => new Promise((resolve, reject) => {
setTimeout(() => resolve('promise3 val'), 3000)
})
// generate执行每一个yield获取数据
function* getAjaxDataStep() {
yield getAjaxData1()
yield getAjaxData2()
yield getAjaxData3()
}
// 一个高阶函数,接受function * 为入参
const autoRunGenerateFn = (genFn) => {
// 得先执行一次,得到迭代函数
const myGen = genFn()
let count = 1;
// 递归执行该函数
// 如果done: false就已知执行autoRun
// 递归出口就是done: true
const autoRun = () => {
const result = myGen.next()
const done = result.done
const value = result.value
if (done) {
// 是否已近完成:完成递归出口
console.log('已完成:', value);
} else {
// 是否已近完成:未完成
if (value instanceof Promise) {
// promise的值
value.then(v => {
// promise的值通过.then获取
console.log(`接受到的值${count}`, v)
count++
// 未全部完成,在promise.then内再次autoRun
autoRun()
})
} else {
// 非promise其值可直接获取
console.log(`接受到的值${count}`, v)
count++
// 未全部完成,再次autoRun
autoRun()
}
}
}
autoRun()
}
autoRunGenerateFn(getAjaxDataStep)
实现效果如下所示:
参考资料
Async
function *