首先我们先来了解几个async基本知识点:
await
命令只能用在
async
函数之中,如果用在普通函数,就会报错
async
函数返回的是Promise 对象
- 如果返回的是一个基本类型数据,
async
函数会先用
Promise.resolve(...)
将其包装为一个Promise对象再将其返回。
- 如果无返回值,等同于直接返回
Promise.resolve()
- 如果返回的是一个Promise对象,则不做处理直接将其返回
await
命令后面可跟Promise对象或者任意表达式
- 如果是Promise对象,则等待并返回该对象的结果
- 如果是表达式,则等待并返回表达式的值
错误处理
如果
await
后面的Promise对象变为
rejected
状态 或者 async方法抛出异常,那么整个
async
函数则会中断执行,并且返回的 Promise 对象为
rejected
状态
-
Promise对象变为
rejected
状态:
async function getTodo() {await new Promise(function (resolve, reject) {reject(\'异步操作出错了\')})console.log(\'柏成大帅比\')return \'柏成\'}getTodo().then(res => console.log(\'res\',res)).catch(err => console.log(\'err\',err))// err,异步操作出错了
-
async方法抛出异常:
async function getTodo() {await new Promise(function (resolve, reject) {resolve(\'异步操作正常\')})throw new Error(\'代码抛出异常\')console.log(\'柏成大帅比\')return \'柏成\'}getTodo().then(res => console.log(\'res\',res)).catch(err => console.log(\'err\',err))// err,Error: 代码抛出异常// at getTodo (js012-测试.html:11)
为了防止代码异常阻塞async函数的执行,我们可以使用
try...catch
方法, 如果
try
方法块中Promise对象变为
rejected
状态 或者 代码抛出异常,则仅仅中断
try
方法块的代码执行,并不会阻塞async函数的后续代码执行
async function getTodo() {try {const val1 = await getFirstVal()const val2 = await getSecondVal()const val1 = await new Promise(function (resolve, reject) {reject(\'异步操作异常\')})} catch (err) {console.log(err);}console.log(\'柏成大帅比\')return \'hello world\'}getTodo().then(res => console.log(res)).catch(err => console.log(err))// 异步操作异常// 柏成大帅比// hello world
实现休眠语法
记得之前做课设要实现一个着色格子板,要看到着色的过程,如何在本次着色后暂停一秒钟再执行下一次着色代码,就采用了下面的休眠方法
// 休眠方法function sleep(interval) {return new Promise(resolve => {setTimeout(()=> resolve(), interval);})}// 调用休眠方法(async function () {for(let i = 1; i <= 5; i++) {console.log(i)await sleep(1000)}})()
继发&并发
继发执行:异步操作存在先后顺序,等待上一个 await 执行完毕后,再接着下一个
async function getTodo() {const val1 = await getFirstVal()const val2 = await getSecondVal()}
并发执行:异步操作相互独立,不存在依赖关系,可以同时触发,提高执行效率
// 如没有await关键字,遇到异步操作,将其挂起无需等待异步操作结果,转而继续执行下一行代码,基本等同于同时触发const firstPromise = getFirstVal()const secondPromise = getSecondVal()const [val1, val2] = await Promise.all([firstPromise, secondPromise]);// 或者const [val1, val2] = await Promise.all([getFirstVal(), getSecondVal()]);
循环中的await
for语句、for/in语句、for/of语句
,会继发执行(等待上一个 await 执行完毕后,再接着下一个),没有任何叼毛问题,nice!
async function getTodo(arr) {for (const item of arr) {const res = await getSomething(item);console.log(`res-${item}:`, res);}console.log(\'finished!\');}getTodo([1,2,3])// res-1:...// res-2:...// res-3:...// finished!
forEach
、
map
,调用了callback回调函数,存在大大的副作用,强烈不建议在
forEach
、
map
等循环ad8中使用
await
async function getTodo(arr) {await arr.forEach(async item => {const res = await getSomething(item);console.log(`res-${item}:`, res);})console.log(\'finished!\');}getTodo([1,2,3])// finished!// res-2:...// res-1:...// res-3:...
从上面代码中我们可以看出两个问题
-
尽管我们在
forEach
循环之前使用了
await
,但是他不会等待
forEach
循环的执行,而是直接打印
finished!
-
三个
getSomething()
异步操作将是并发执行,请求时间短的操作将被率先打印,和请求顺序无关
参考文档
阮一峰 async函数
for 循环里的 await