属于,async是es6的新特性,用于表明程序里面可能有异步过程。用async关键字声明的函数返回的是一个Promise对象,如果在函数中return一个直接量,async会把这个直接量通过Promise.resolve()封装成Promise对象;当async函数没有返回值时,返回“Promise.resolve(undefined)”。
本教程操作环境:windows7系统、ECMAScript 6版、Dell G3电脑。
ES6新特性 async和await关键字
1、初步了解
我们先从字面意思来理解这两个关键字,async是asynchronous(异步)的简写,而await可以认为是async wait的简写。所以async可以理解为用于声明一个函数是异步的,而await用于等待一个异步任务执行完成。
async和await关键字让我们可以用一种更简洁的方式写出基于promise的异步行为,而无需刻意地链式调用promise。
接下来我们通过先几个例子,初步了解一下async和await的作用。
知识点1: 用 async 关键字声明的函数返回的是一个 Promise 对象。如果在函数中 return
一个直接量,async 会把这个直接量通过 Promise.resolve()
封装成 Promise 对象。当 async
函数没有返回值时,返回 Promise.resolve(undefined)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
1 2 3 4 5 6 7 8 9 10 11 |
|
知识点2: await关键字只能使用在被async声明的函数内,用于修饰一个Promise对象,使得该Promise对象处理的异步任务在当前协程上按顺序同步执行。
1 2 3 4 5 6 7 8 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
2、async关键字
(1)用于表明程序里面可能有异步过程
(2)async函数返回值的类型为Promise对象: 这是和普通函数本质上不同的地方,也是使用时重点注意的地方;
- return newPromise( ),这个用法符合async函数本意;
- return data,特别注意到是这样子写相当于Promise.resolve(data),返回的data被封装成一个Promise对象,但是在调用async函数的地方通过简单的=是拿不到这个返回值data的,因为返回值是一个Promise对象,所以需要用.then(data => { })方式才可以拿到这个data;
- 如果没有返回值,相当于返回了Promise.resolve(undefined);
(3)无等待,非阻塞:使用async关键字声明的函数里面如果有异步过程可能会等待,但是函数本身会马上返回,不会阻塞当前主线程。如果在函数里面使用了await关键字修饰的异步过程,其工作在相应的协程上,会阻塞等待异步任务的完成再返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
3、await关键字
(1)await只能在async函数内部使用:不能放在普通函数里面,否则会报错。
(2)await关键字后面跟的是一个Promise对象。如果跟的是一个函数,则这个函数应当返回一个Promise对象。如果跟的是非Promise对象,则会通过Promise.resolve( )函数自动将这个东西包装成一个Promise对象并置于fulfilled状态。
1 2 3 4 5 6 |
|
(3)await的本质是等待它所修饰的Promise对象的fulfilled状态,并把resolve(data)的数据data返回。
意思是,如果await后面跟的是一个 Promise
对象,await
就会阻塞后面的代码,等着 Promise
对象 resolve
,然后得到 resolve
的值,作为 await
表达式的运算结果。
1 2 3 4 5 6 7 8 9 10 |
|
(4)await并不关心它所修饰的Promise对象的rejected状态,即reject(data)的数据data并不会被await处理,所以建议通过Promise对象调用catch去捕获。
1 2 3 4 5 6 |
|
4、深入讲解async和await
(1)执行顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
①首先打印输出getResult调用前
,同步代码,顺序执行;
②然后调用方法getResult( ),打印输出double调用前
,同步代码,顺序执行;
③再调用异步方法double( )
如果此处没有使用await关键字修饰,则依次输出的是:getResult调用前、double调用前、double调用后、getResult调用后、1s后输出20
因为异步操作不会影响其他代码的执行,所以会将其他代码按顺序执行完,最后再执行double函数
因为这里使用了await关键字,所以getResult( )的代码执行到这里就会被阻塞,等到double函数resolve了,再往下执行
④尽管getResult函数内部被await阻塞了,由于getResult函数本身也是个async函数,所以它不会影响getResult函数外面的代码执行。因为调用async函数不会造成阻塞,它内部的所有阻塞都被封装在一个Promise对象中异步执行。
⑤所以在调用getResult函数后,会继续向下执行,即打印输出getResult调用后
⑥当1s之后,异步函数double执行完成,将结果交给resolve。
⑦通过await关键字接收到double函数resolve的值,赋值给result变量。打印输出20
⑧因为使用了await阻塞将异步变为同步,所以在打印输出20后再打印输出double调用后
(2)处理reject回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
(3)使用await优化Promise对象的回调地狱问题
在Promise章节中我们通过了Promise对象的then( )方法链式调用解决了回调地狱问题,但看起来仍然不够美观,我们可以通过await优化一下,让它看起来更符合我们平时代码的编写习惯。
1 2 3 4 5 6 7 8 9 |
|
原本的要求是每个请求都依赖于上一个请求的返回值,那么是不是得等一个请求完,才能发送下一个请求?这时我们可以思考一下,await的作用是什么?是不是对一个Promise对象去进行阻塞,使其状态变为fulfilled后获取resolve的值。这不就正是我们所需要的。
1 2 3 4 5 6 |
|