blog-web/source/_posts/JavaScript/Promise(3)-async与await.md
2018-12-07 17:45:58 +08:00

140 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: Promise(3)-async与await
date: 2018-12-6 15:29:07
tags:
- JavaScript
- Promise
categories:
- JavaScript
---
### async的作用
首先`async`关键字是用来修饰函数的
带有async的函数, 调用的返回值必定是一个Promise对象
1. 如果本身返回的就是Promise对象, 则不会做任何处理
2. 如果本身返回的不是Promise对象, 则会用`Promise.resolve`包装后再返回
<!-- more -->
```javascript
async function test1() {
return '123'
}
const p1 = test1()
// 等价于如下写法
function test2() {
return '123'
}
const p2 = Promise.resolve(test2())
```
### await的作用
`await`是用来修饰**表达式**的
表示**等待这个表达式的执行结果**
限制就是必须使用在async函数当中, 否则就会报错
![await](/images/JavaScript/await.png)
表达式的结果可以是Promise对象, 也可以不是
+ 不是promise对象 : await会阻塞后面的代码先执行await所在的async函数调用之后的同步代码同步代码执行完再回到async内部把这个非promise的东西作为 await表达式的结果
例如
```javascript
console.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 表达式的运算结果。
例如
```javascript
async 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
---
### 综合运用
```javascript
async function async1() {
console.log( 'async1 start' ) // (3)
await async2() // (4)
console.log( 'async1 end' ) // (5)
}
async function async2() {
console.log( 'async2' ) // (6)
}
console.log( 'script start' ) // (1)
setTimeout( function () {
console.log( 'setTimeout' ) // (2)
}, 0 )
async1()
new Promise( function ( resolve ) {
console.log( 'promise1' ) // (7)
resolve();
} ).then( function () {
console.log( 'promise2' ) // (8)
} )
console.log( 'script end' ) // (9)
```
执行过程分析:
1. 开始是async1和async2两个函数定义, 但是没有调用, 所以先跳过, 首先执行的肯定是(1), 输出**script start**
2. 然后是一个定时器, 那么此时会吧定时器的回调函数加入到任务队列当中
> 此时的task队列 _[ (2) ]_
3. 然后是async1的调用, 会先执行(3), 输出**async1 start**
4. 执行到(4)时, 会阻塞之后的内容, 也就是(5), 并加入任务队列
但是async2函数内部的内容不会阻塞, 还是同步执行, 执行(6), 输出**async2**
> 此时的micro task队列 _[ (5) ]_
5. 之后创建Promise对象, 构造方法传入的函数是立即执行的, 执行(7), 输出**promise1**
6. resolve的执行加入到任务队列, 也就是(8)
> 此时的micro task队列 _[ (5) (8) ]_
7. 最后的(9)属于主线, 所以直接执行, 输出**script end**
8. 主线执行完毕, 开始执行任务队列当中的内容, 首先会执行micro task队列的内容
依次输出**async1 end**和**promise2**
9. micro task队列出队执行完毕, 开始执行新的task队列(由setTimeout创建的)
所以输出**setTimeout**
> 最终的输出结果是:
```
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
```
说明: 不同的js引擎的实现, async1 end和promise2的顺序可能有差别, 其他的顺序都是一样的