Async函数已写入ES7标准中,通过async函数可以更友好直观的写异步代码。调用Async函数时返回的是一个Promise对象,所以,介绍Async函数之前,有必要先介绍下Promise对象。
Promise对象
为什么要用
在实际工作中,经常会遇到这样的场景:接口A需要的入参是接口B响应中的部分或全部内容,这时在执行请求接口A的代码时就需要等待接口B响应完成以后再执行,这就涉及异步调用。
异步调用最开始是通过回调函数进行的,但当代码依赖增多时,回调函数的写法就会变得难以维护(比如极端情况,接口A的入参依赖接口B的响应,接口B的入参依赖接口C的响应,接口C的入参依赖接口D的响应…)。而Promise对象就是解决回调函数地狱(callback hell。回调函数本身没有问题,但嵌套多个回调函数就变得难以维护了)的问题:它可以用类似写同步代码的模式写异步调用。
这里异步调用不是说JS可以异步执行:JS是单线程(single-threaded),总是同步执行,但有多种多样的回调机制(callback mechanisms)。通过回调机制,可以实现异步操作。注意,回调函数只是多种多样的回调机制中的一种,正在介绍的Promise对象和将要介绍的Async函数都是回调机制的一种。
定义
promise
是一个对象,它可以用于表示一个异步操作的执行结果(成功或者失败)并将执行结果返回。
promise,顾名思义,“承诺”将来的某个时间点返回结果。
语法
new Promise(function(resolve, reject) { ... }); |
既然promise
是对象,便可通过构造函数创建promise
实例,Promise构造函数接受一个函数作为参数,而作为Promise的入参,该函数又接受两个函数作为入参:其中第一个函数(通常命名为resolve
)在promise
返回预期结果时调用,第二个函数(通常命名为reject
)在不能如期返回结果时调用。
为便于描述,以下如预期结果称为resolve的情况,不然称为reject的情况。
常用方法then()
与catch()
通常,promise 实例和then()以及catch()方法一起使用。
then()
方法返回一个promise
,最多接受两个函数作为参数,分别处理resolve和reject两种情况:第一个参数是promise resolve时执行的回调函数,并且resolve的值作为入参传入回调函数;第二个参数是promise reject时调用的回调函数,并且将reject的原因作为参数传入回调函数。
更常用的是resolve时使用then()方法,reject时使用catch()方法。
语法如下:
p.then(onFulfilled[, onRejected]); |
catch()
方法也返回一个promise
,但只接受一个函数作为参数,只在reject时执行。
语法如下:
p.catch(onRejected); |
举几个例子
- 模拟异步请求:生成随机数,如果随机数小于1则认为是resolve的情况,执行resolve函数;如果大于1,则认为reject,执行reject函数。
let p = new Promise(function (resolve, reject) { |
- 模拟promise强大之处:链式调用
let p = new Promise(function (resolve, reject) { |
Async函数
为什么要用
Promise对象相比回调函数已直观很多:将横向发展的回调函数变成了链式的写法。但Promise对象在编写和理解上实际并没有那么轻松。
而调用Async函数时会自动返回一个Promise对象:当这个异步函数返回预期值时,Promise会调用resolve方法处理这个预期值;如果这个异步函数抛出异常或者返回非法值时,Promise会调用reject方法进行处理。
虽然依然使用Promise,但这一切都是交给Async函数去处理,我们不需要编写代码处理Promise对象,只关注返回值就好了。
语法
Async函数本身的语法为:
async function name([param[, param[, ... param]]]) { |
在普通函数前面加async
关键字就变成了Async函数。
await
表达式
通常Async函数都会搭配await
表达式和try-catch
语句一起使用,代码格式为:
async function myFirstAsyncFunction() { |
关于await
表达时的几点说明:
- await表达式必须放在Async函数中使用;
- await表达式后面可以跟一个Promise对象或者任何待解析的值(通常都是promise对象,不然没啥意义了),在等待Promise返回值时,Async函数暂停执行
await
表达式后面的代码,但不会阻塞JS主线程,即Async函数后面的代码正常执行。
具体参见下面重写的promise例子。
重写promise的例子
- 模拟异步请求
// 模拟异步请求生成的promise对象 |
- promise链式写法重写
let p = new Promise(function (resolve, reject) { |
参考资料
【1】Promise
【2】MDN-Promise
【3】ES6 Promises
【4】async function
【5】Async functions - making promises friendly
【6】单线程模型
【7】JavaScript 既是单线程又是异步的,请问这二者是否冲突,以及有什么区别?
【8】Javascript异步编程的4种方法
【9】When is JavaScript synchronous?