前言:
Async/Await应该是目前最简单的异步方案了,ES7 中新增了 async/await 两个关键词async
顾名思义是“异步”的意思,用于声明一个函数是异步的。而await
从字面意思上是“等待”的意思,就是用于等待异步完成。
主体:
(1)同步操作
结果为1、3、2,这便是同步状态下的执行顺序
(2)“async函数关键字”基本用法
首先了解下async“异步”关键字的返回值,如下所示
这里可以看到函数实际返回Promise {<resolved>: undefined},接下来做下修改,在函数内部加个return
此时返回值为Promise {<resolved>: "测试"}.
async函数是声明一个异步函数,由此可以分析出async返回值为一个Promise对象。因此我们可以使用then方法添加回调函数,从而处理async
函数返回的结果。
上面写法直接return返回一个直接量,等于直接去调用Promise.resove()这个方法。而Promise.resolve方法也就是生成一个Promise实例,并且其直接调用resolve。
上面代码等效写法如下
至此,我们可以得出两个结论:
1、async
函数返回一个 Promise
实例,可以使用then
方法(为返回的Promise实例)添加回调函数
2、如果函数return 一个直接量,那么就等于直接去调用Promise.resolve()方法
如果上面步骤看着繁琐,那么试下下面的案例,便可以清楚该两个结论
(3)awiat“等待”关键字作用
await是配合async使用的,译为等待,实际为等待async执行结果
回到之前的案例
之前也说过,如果return返回一个直接量,等于直接去调用Promise.resove()这个方法。而Promise.resolve方法也就是生成一个Promise实例,并且其直接调用resolve。
上面写法相当于
或者
所以可以得出结论三:正常情况下,await
命令后面是一个 Promise
对象。当然也可以是原始类型和非Promise对象,但会被转成一个立即resolve
的 Promise
对象,这是等同于同步操作。
接下来测试awiat作用,概括如下
await必须配合async使用,但是await的对象可以不是Promise对象,一个普通的函数也可以使用。 1、如果它等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。 2、如果它等到的是一个 Promise 对象,await 就忙起来了,它会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。
但是async函数不会造成阻塞,所以await配合async使用,则没有影响到外部。
1、await等到的Promise对象,但里面没有异步操作,所以不会阻塞
接下来测试下是否阻塞
会看到没有发生阻塞,直接依次执行马上输出1、2、测试1
2、接下来在Promise里添加异步操作,如下所示
此时结果为,先输出1,,,,然后等待2S后返回async结果后输出2和测试1
如果之前案例有些繁琐,看看下面这个例子
此外,还可以将async放在IIFE函数里调用,进行案例测试
(4)异常处理
结论四:如果await后面的异步操作出错,那么等同于async函数返回的 Promise 对象被reject。
捕获错误写法如下:如果await
后面的异步操作出错,那么等同于async
函数返回的 Promise
对象被reject
,所以最好把await
命令放在try...catch
代码块中。
(5)注意点:await
关键字只能用于async
函数执行上下文中
(6)其他案例
如果之前案例,都看不懂,那么再来个案例
案例1:
function p1(){ return new Promise(function(resolve,reject){ setTimeout(function(){ console.log("p1_2000"); resolve() },3000) }) } function p2(){ return new Promise(function(resolve,reject){ setTimeout(function(){ console.log("p2_2000"); resolve() },2000) }) } console.log("start"); p1(); p2(); console.log(end) //start //end //p2_2000 //p1_3000
案例2:接下来做的是让他按这个顺序执行下来咱们就要用到async、await
function p1(){ return new Promise(function(resolve,reject){ setTimeout(function(){ console.log("p1_3000"); resolve() },3000) }) } function p2(){ return new Promise(function(resolve,reject){ setTimeout(function(){ console.log("p2_2000"); resolve() },2000) }) } //await 只能出现在异步函数里 async function shi(){ console.log("start") await p1(); await p2(); console.log("end") } p3=shi(); p3.then(function(){ console.log("结束") })
上边函数执行下来就是按照那个顺序下来的,大家记住await 只能出现在异步函数里!配套使用
案例3:
(7)状态变化
(8)优化耗时,同时触发设置
测试上述代码,结果为
4S后返回“读取数据111”
在此基础上,再过3S返回“读取数据222”
两个独立的异步操作(即互不依赖),被写成继发关系(只有执行完one操作,才能去执行two操作)。这样比较耗时,因为只有one
完成以后,才会执行two
,完全可以让它们同时触发。
解释:
解释:这里的one和two方法会返回两个Promise实例(假设是发起Ajax请求,请求one和two的内容),只有执行了方法,对应的操作才会执行,如果写成上面的形式,
就会导致执行完one的操作后(等待收到服务器的响应后),才能执行two的操作,这样就成了同步,比较耗时,因此可以将上面的写法修改,使得在等待one执行完的时间内
(在等待服务器响应的期间)去执行执行two
此时结果为同时输出(因为第二个结果耗时短,所以运行完后会一直等待第一个执行,而后一起输出),都是同时触发,这样就会缩短程序的执行时间
上述利用了数组元素解构赋值和Promise.all