定义
- async 是异步的意思,await则可以理解为 async wait。所以可以理解async就是用来声明一个异步方法,而 await是用来等待异步方法执行
- async作为一个关键字放在函数前面,表示该函数是一个异步函数,异步函数意味着该函数的执行不会阻塞后面代码的执行;而 await 用于等待一个异步方法执行完成;
- 当函数内部执行到一个 await 语句的时候,如果语句返回一个 promise 对象,那么函数将会等待 promise 对象的状态变为 resolve 后再继续往下执行。并会阻塞该函数内后面的代码。
- 因此async/await的作用就是将异步逻辑,转化为同步的顺序来书写,并且这个函数可以自动执行。
- 为了优化 .then 链而开发出来的。
一、关于async
- async的用法,语法很简单,在函数前面加上async关键字,表示函数是异步的。它只有一个作用,他的调用会返回一个promise对象。
async function demo1() {
return '1'
}
async function demo2() {
throw new Error('rejected');
}
async function demo3() {
for (let index = 0; index < 3; index++) {
}
}
async function demo4() {
return '1'
}
console.log(demo1())
console.log(demo2())
console.log(demo3())
demo4().then(val => {
console.log(val)
})
console.log(2)
结果:
发现 timeout() 函数虽然调用了,但是没打印1,而是返回了一个promise 对象,并且还有state和result。
如果async函数中有返回值,当调用该函数时,内部会调用Promise.resolve()把返回值转化成一个promise对象返回。
如果async内部抛出错误,那么会调用Promise.reject()返回一个promise对象。
如果想获取到async 函数的执行结果,调用promise的then 或 catch 即可。
如果async 函数执行完,返回的promise 没有注册回调函数,比如函数内部做了一次for 循环,你会发现函数的调用,就是执行了函数体,和普通函数没有区别,唯一的区别就是函数执行完会返回一个promise 对象。
二、关于await
async函数的执行会返回promise对象,并且把内部的值进行promise的封装。如果promise对象通过then或catch方法又注册了回调函数,async函数执行完以后,注册的回调函数就会放到异步队列中,等待执行。
如果只是async,和promise差不多,但有了await就不一样了,await关键字只能放到async函数里面,await是等待的意思,那么它等待什么呢?它后面跟着什么呢?
其实await不仅仅用于等Promise对象,还可以等任意表达式,所以await后面实际是可以接普通函数调用或者直接量的,不过我们更多的是放一个返回promise 对象的表达式。他等待的是promise对象执行完毕,并返回结果。
//所以下面这个示例完全可以正确运行
function getSomething () {
return 'something'
}
async function testAsync () {
return Promise.resolve('hello async')
}
async function test () {
const v1 = await getSomething()
const v2 = await testAsync()
console.log(v1, v2)
}
test()
如果await等到的不是一个Promise对象,那么await表达式的运算结果就是它等到的东西。
如果它等到的是一个Promise对象,await就忙起来了,它会阻塞函数后面的代码
,等着Promise对象resolve,然后得到resolve的值,作为await表达式的运算结果。
**注意:**如果我们使用await
,都知道需要给当前函数添加async
标识。但是有一种特殊情况:
function takeLongTime () {
return new Promise(resolve => {
setTimeout(() =>
resolve('long_time_value'),
1000
)
})
}
async function test () {
let v = await takeLongTime()
console.log(v, 'v')
}
test()
可以发现takeLongTime()
没有加async,实际上takeLongTime ()
本身就返回Promise对象,加不加async结果都一样。
await 优势在于处理 then 链,使代码看起来像同步代码一样,下面是实例应用
写一个async 函数,从而可以使用await 关键字, await 后面放置的就是返回promise对象的一个表达式,所以它后面可以写上 doubleAfter2seconds 函数的调用
// 2s 之后返回双倍的值
function doubleAfter2seconds (num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num * 2)
}, 2000)
})
}
async function testResult() {
let first = await doubleAfter2seconds(30);
let second = await doubleAfter2seconds(50);
let third = await doubleAfter2seconds(30);
console.log(first + second + third);
}
testResult()
6秒后,控制台输出220, 我们可以看到,写异步代码就像写同步代码一样了,再也没有回调地域了。
**注意:**这里强调一下,当js引擎在等待promise.resolve的时候,他并没有真正的暂停工作,它可以处理其他的一些事情,如果我们在testResult函数后面继续执行其他代码,比如console.log一下,会发现console.log代码先执行。
三、案例
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
setTimeout(() => {
console.log('timer1')
}, 0)
}
async function async2() {
setTimeout(() => {
console.log('timer2')
}, 0)
console.log('async2')
}
async1()
setTimeout(() => {
console.log('timer3')
}, 0)
console.log('start')
// async1 start => async2 => start => async1 end => timer2 => timer3 => timer1
首先进入async1,打印出 async1 start。
然后遇到async2,进入async2,遇到定时器 timer2,加入宏任务队列,之后打印 async2。
由于 async2阻塞了后面的代码执行,将后面的代码加入微任务队列。所以执行后面的定时器 timer3,将其加入宏任务队列,之后打印同步代码 start。
同步代码执行完毕,先执行微任务队列,打印出 async1 end,遇到定时器 timer1,将其加入宏任务队列。
最后再执行宏任务队列,宏任务队列有3个任务,先后顺序是 timer2,timer3,timer1,分别按顺序执行,任务队列按照先进先出原则执行。
四、总结
async 函数 1)函数的返回值为Promise对象 2)Promise对象的结果由async函数执行的返回值决定
await 表达式 1)正常情况下,await右侧的表达式一般为 promise对象 , 但也可以是其它的值 2)如果表达式是promise对象,await就忙起来了,它会阻塞函数后面的代码,等着Promise对象resolve,然后得到resolve的值,作为await表达式的运算结果。 3)如果表达式是其它值, 直接将此值作为await的返回值
async和await基于promise的。使用async的函数将会始终返回一个 promise 对象。这一点很重要,要记住,可能是你遇到容易犯错的地方。
在使用await的时候我们只是暂停了函数,而非整段代码。这里经常会是容易犯错的地方。
async和await是非阻塞的
仍然可以使用 Promise,例如Promise.all(p1, p2, p3).,接受一个数组作为参数,p1、p2、p3 都是 Promise 实例,如果不是,就会先调用 Promise .resolve方法,将参数转为 Promise 实例,再进一步处理。只要 p1、p2、p3 之中有一个被 rejected,整个状态就变成 rejected。
注意 1)await必须写在async函数中, 但async函数中可以没有await 2)如果await的promise失败了, 就会抛出异常, 需要通过try…catch来捕获处理