ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

async函数的定义方式

// 函数声明
async function fn() {}// 函数表达式
const fn = async function () {}// 箭头函数
const fn = async () => {}// 对象的方法
let obj = { async fn() {} }
obj.fn().then(...)

核心用法

  • await命令只能用在async函数之中,如果用在普通函数中会报错。

  • async函数体内,await命令之前的代码以及await命令所在语句中await后的代码都为正常的同步代码,在async函数调用时正常执行。

  • async函数会返回一个Promise对象。若返回的不是一个显式的Promise对象,则会使用Promise.resolve()包装成Promise对象。

  • await命令后跟一条返回Promise对象的语句。若语句返回的不是Promise对象,则会使用Promise.resolve()包装成Promise对象。

  • await命令相当于其后语句返回的Promise对象调用的只有一个回调函数的then方法。async函数体内await命令所在语句后的语句相当于then方法第一个回调函数中的语句,await关键字左侧返回的值相当于要传入then方法第一个回调函数的参数。

  • await后语句返回的Promise对象的执行结果可能是rejected,所以最好把await命令放在try...catch代码块中。

示例代码:

async function fn() {return 100 // 相当于 return Promise.resolve(100)
}
console.log(fn()) // Promise {<fulfilled>: 100}
(async function () {const a = await new Promise((resolve, reject) => {console.log('start')resolve('hello')console.log('middle')}).then((val) => {console.log(val)return 'world'})console.log(a)
})()
console.log('end')// start
// middle
// end
// hello
// world

错误处理

因为async函数体内await命令所在语句后的语句相当于then方法第一个回调函数中的语句,所以如果任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行。

(async function () {await Promise.reject('出错了')await Promise.resolve('hello world'); // 不会执行
})()
console.log('hello') // 仍然可以执行

常见错误方式

1. await后的Promise变为reject状态

await命令后面的 Promise 对象如果变为rejected状态,则reject的参数会被catch方法的回调函数接收到。

(async function () {await Promise.reject('出错了')console.log('hello'); // 不会执行
})().catch(e => console.log('err~', e))
// err~ 出错了

注意,上面代码中,await语句前面没有return,但是reject方法的参数依然传入了catch方法的回调函数。这里如果在await前面加上return,效果是一样的。

2. await后的异步操作出错

Promise内部直接抛出错误(如以下的throw new Error('出错了')),则Promise状态会变为rejectedreject的参数即为抛出的错误对象(如以下的Error('出错了')

(async function () {await new Promise(() => {throw new Error('出错了')console.log('hello') // 不会执行})
})().catch(e => console.log('err~', e))
// err~ Error: 出错了

3. async函数内部抛出错误

async函数内部直接抛出错误,会导致async函数返回的 Promise 对象变为rejected状态。抛出的错误对象会被catch方法回调函数接收到。

(async function () {throw new Error('出错了');console.log('hello'); // 不会执行
})().catch(e => console.log('err~', e))
// err~ Error: 出错了// 类似于如下
new Promise(() => {throw new Error('出错了')console.log('hello'); // 不会执行
}).catch(e => console.log('err~', e))

错误处理方式

如果希望即使一个异步操作失败,也不要中断后面的异步操作,可以将可能出错的await放在try...catch结构里面,这样不管这个异步操作是否成功,后面的await都会执行。

async function f() {try {await Promise.reject('出错了')} catch (e) {}return await Promise.resolve('hello world')
}f().then(v => console.log(v))
// hello world

如果await后跟的是Promise对象,则也可以后面的 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

如果有多个await命令,可以统一放在try...catch结构中。

async function main() {try {const val1 = await firstStep();const val2 = await secondStep(val1);const val3 = await thirdStep(val1, val2);console.log('Final: ', val3);} catch (err) {console.error(err);}
}

Promise对象的状态变化

async函数返回的 Promise 对象,必须等到async函数内部所有await命令后面的 Promise 对象执行完,才会发生状态改变,除非遇到return语句或者抛出错误。也就是说,只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数。

示例代码:

function getSquare(num) {return new Promise((resolve) => {setTimeout(() => {resolve(num ** 2)}, 1000)})
}
async function fn(num) {const val1 = await getSquare(num)const val2 = await getSquare(val1)return val2
}
fn(3).then(console.log)
// 延迟两秒后,打印81

上面代码中,函数fn内部有两个操作:将输入平方、将得到的结果再次平方。只有这两个操作全部完成,才会执行then方法里面的console.log

继发与并发

多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。

let foo = await getFoo();
let bar = await getBar();

上面代码中,getFoogetBar是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo完成以后,才会执行getBar,完全可以让它们同时触发。

如下写法,getFoogetBar是同时触发,这样就会缩短程序的执行时间。

let [foo, bar] = await Promise.all([getFoo(), getBar()]);

示例代码:

// 定义求平方函数
function getSquare(num) {return new Promise((resolve) => {setTimeout(() => {resolve(num ** 2)}, 1000)})
}// 继发处理
!(async function () {const arr = [1, 2, 3]const res = []for (const num of arr) {res.push(await getSquare(num))}console.log(res)
})()
// 延时3秒后,打印[1, 4, 9]// 并发处理
!(async function () {const arr = [1, 2, 3]const promiseArr = arr.map(x => getSquare(x))let res = await Promise.all(promiseArr)console.log(res)
})()
// 延时1秒后,打印[1, 4, 9]

遍历

数组的forEach()map()等方法都不支持按顺序等待异步处理(即不支持上述继发处理),forfor...infor...of 都会按顺序等待异步。

示例代码:

function getSquare(num) {return new Promise((resolve) => {setTimeout(() => {resolve(num ** 2)}, 1000)})
}!(function () {const arr = [1, 2, 3]arr.forEach(async num => {console.log(await getSquare(num));})
})()
// 延时1秒后,同时打印1 4 9!(async function () {const arr = [1, 2, 3]for (const num of arr) {console.log(await getSquare(num));}
})()
// 延时1秒打印1,再延时1秒打印4,再延时1秒打印9

注意事项

  • await命令只能用在async函数之中,如果用在普通函数中会报错。

  • async函数体内,await命令之前的代码以及await命令所在语句中await后的代码都为正常的同步代码,在async函数调用时正常执行。

  • await命令后的Promise对象的运行结果可能是rejected,所以最好把await命令放在try...catch代码块中。

async/await用法详解相关推荐

  1. ES6中的promise、async、await用法详解

    <!DOCTYPE html> <html> <head><title>Promise.async.await</title> </h ...

  2. Python中下划线 _ 的最全用法详解

    Python中下划线 _ 的最全用法详解 '_'是什么? _在python中可以作为一个标识符,用于定义变量和方法唯一名称.同时它也是Python中的一个软关键字,指在某些特定上下文中保留的关键字.截 ...

  3. linux mount命令参数及用法详解

    linux mount命令参数及用法详解 非原创,主要来自 http://www.360doc.com/content/13/0608/14/12600778_291501907.shtml. htt ...

  4. async与awite详解

    async与awite详解 使用async与awite的目的:使用async和await能够使我们的异步代码书写,更像是在使用 同步代码那样,不会出现层层嵌套 注:promise类型对象有两个值,[[ ...

  5. html js require函数,Require.js的基本用法详解

    一:什么是require.js ①:require.js是一个js脚本加载器,它遵循AMD(Asynchronous Module Definition)规范,实现js脚本的异步加载,不阻塞页面的渲染 ...

  6. CyclicBarrier 用法详解

    CyclicBarrier 用法详解 CyclicBarrier使用场景 用于协调多个线程同步执行操作的场合,所有线程等待完成,然后一起做事情( 相互之间都准备好,然后一起做事情 ) 例如百米赛跑,必 ...

  7. Require.js的基本用法详解

    一:什么是require.js ①:require.js是一个js脚本加载器,它遵循AMD(Asynchronous Module Definition)规范,实现js脚本的异步加载,不阻塞页面的渲染 ...

  8. CountDownLatch用法详解

    CountDownLatch用法详解 CountDownLatch使用场景 线程计数器 用于线程执行任务,计数 等待线程结束 用法一: 等待所有的事情都做完 //程序计数器CountDownLatch ...

  9. python argv 详解_Python3 sys.argv[ ]用法详解

    sys.argv[]说白了就是一个从程序外部获取参数的桥梁,这个"外部"很关键,因为我们从外部取得的参数可以是多个,所以获得的是一个列表(list),也就是说sys.argv其实可 ...

最新文章

  1. 01 Java面试之控制类
  2. python起步输入-第 4 节 小Python 起步
  3. CentOS学习笔记 - 7. jekins安装
  4. imp 只导入索引_使用imp导入表和索引至不同表空间方法
  5. 微软宣布推出Azure Blockchain Tokens加密代币平台
  6. NetCat Tutorials
  7. 【android开发】如何在Linux平台下安装JDK环境
  8. mysql begin operations_MySQL入门(七):More JOIN operations
  9. clickhouse原理解析与应用实践 pdf_阿里专家分享内部绝密RocketMQ核心原理与最佳实践PDF...
  10. 使用Jenkins自动部署博客
  11. centos mysql开发包_CentOS 7 安装 MySQL-阿里云开发者社区
  12. oracle11g 时间失效,关于oracle11g RAC 的CTSS与ntp时间同步的疑问
  13. 关于VS2017提示I/O文件操作函数需要加上_s的解决办法
  14. 树莓派4b-centos操作系统安装包
  15. Linux查看内存的方法
  16. (翻译)按钮的对比色引导用户操作的方式
  17. 【ThreeJS基础教程-高级几何体篇】2.5 加载GLTF/GLB格式文件,Draco压缩文件的获取与加载
  18. 科技如何激发女权主义并改变男性气质
  19. 《第五项修炼》,读后感
  20. C语言--指针之空指针(void *)

热门文章

  1. 手把手带你设计接口自动化测试用例(二):根据接口信息设计测试用例
  2. Python实现自动生成测试用例
  3. 2.7.2_剪纸图形
  4. Mac怎么配置maven环境
  5. PHP做中文分词技术
  6. java jedispool实例_通过JedisPool获取Jedis示例
  7. CSS3 Media Queries 实现网页自适应
  8. AtomicLong
  9. hifi下载_如何配置HiFi桌面音频系统
  10. 协众信息技术设计找工作难?来看看~