async的作用
首先async
关键字是用来修饰函数的
带有async的函数, 调用的返回值必定是一个Promise对象
- 如果本身返回的就是Promise对象, 则不会做任何处理
- 如果本身返回的不是Promise对象, 则会用
Promise.resolve
包装后再返回
1 | async function test1() { |
await的作用
await
是用来修饰表达式的
表示等待这个表达式的执行结果
限制就是必须使用在async函数当中, 否则就会报错
表达式的结果可以是Promise对象, 也可以不是
不是promise对象 : await会阻塞后面的代码,先执行await所在的async函数调用之后的同步代码,同步代码执行完,再回到async内部,把这个非promise的东西,作为 await表达式的结果
例如1
2
3
4
5
6
7
8console.log(1);
(async function(){
/* 存在await导致该函数异步执行, 所以会先输出之后的3 */
console.log(await 4); //阻塞之后的2
console.log(2);
})();
console.log(3);
/* output: 1 3 4 2 */正因为await会阻塞之后的代码, 所以才必须放在async函数当中
是promise对象 : await 也会阻塞async后面的代码,先执行async外面的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果。
例如1
2
3
4
5
6
7
8
9
10
11
12async function test() {
console.log(4)
return 1
}
(async function(){
// 因为test函数是async修饰的, 所以返回的是Promise对象
console.log(await test())
console.log(2)
})()
console.log(3)
/* output: 4 3 1 2 */
setTimeout与Promise
Promise是一个micro task
,我们的主线程是一个task
。micro task会在task后面执行,然后才会接着执行下一个task。
而setTimeout的返回函数是一个新的task,所以这里Promise的执行会先于新task执行
简单来说就是
setTimeout的队列与promise的队列不是同一个队列,执行的优先级低于promise
综合运用
1 | async function async1() { |
执行过程分析:
开始是async1和async2两个函数定义, 但是没有调用, 所以先跳过, 首先执行的肯定是(1), 输出script start
然后是一个定时器, 那么此时会吧定时器的回调函数加入到任务队列当中
此时的task队列 [ (2) ]
然后是async1的调用, 会先执行(3), 输出async1 start
执行到(4)时, 会阻塞之后的内容, 也就是(5), 并加入任务队列
但是async2函数内部的内容不会阻塞, 还是同步执行, 执行(6), 输出async2此时的micro task队列 [ (5) ]
之后创建Promise对象, 构造方法传入的函数是立即执行的, 执行(7), 输出promise1
resolve的执行加入到任务队列, 也就是(8)
此时的micro task队列 [ (5) (8) ]
最后的(9)属于主线, 所以直接执行, 输出script end
主线执行完毕, 开始执行任务队列当中的内容, 首先会执行micro task队列的内容
依次输出async1 end和promise2micro task队列出队执行完毕, 开始执行新的task队列(由setTimeout创建的)
所以输出setTimeout
最终的输出结果是:
1 | script start |
说明: 不同的js引擎的实现, async1 end和promise2的顺序可能有差别, 其他的顺序都是一样的