### 异步基础
#### 单线程和异步
* JS 是单线程语言,只能同时做一件事
* 浏览器和 nodejs 已支持 JS 启动**进程**,如 Web Worker
* JS 和 DOM 渲染共用同一个线程,因为 JS 可修改 DOM 结构
* 异步不会阻塞代码运行,同步会阻塞代码执行
#### 回调地狱-callback hell
![image-20210304133154583](https://image.mdashen.com/pic/image-20210304133154583.png)
#### Promise 基础
![image-20210304133507684](https://image.mdashen.com/pic/image-20210304133507684.png)
![image-20210304133521043](https://image.mdashen.com/pic/image-20210304133521043.png)
### 异步进阶
#### JS 如何执行
* 从前到后,一行一行执行
* 如果某一行执行错误,则停止下面代码的执行
* 先把同步代码执行完,在执行异步
#### event loop(事件循环/事件轮循)
> JS 是单线程运行的,异步要基于回调来实现;event loop 就是异步回调的实现原理
![image-20210304181153409](https://image.mdashen.com/pic/image-20210304181153409.png)
* event loop 过程
* 同步代码,一行一行放在 Call StacK 执行
* 遇到异步,会先“记录”下,等待时机(定时、网络请求)
* 时机到了,就移动到 Callback Queue
* 如 Call Stack 为空(即同步代码执行完)Event Loop 开始工作
* 轮询查找 Callback Queue,如有则移动到 Call Stack 执行
* 然后继续轮询查找(永动机一样)
##### DOM 事件和 event loop
* 异步(setTimeout,ajax等)使用回调,基于 event loop
* DOM 事件(dom事件不是异步)也使用回调,基于 event loop
#### Promise 进阶
* Promise 的三种状态
* pending
* resolved
* rejected
* resolved 触发 then 回调,rejected 触发 catch 回调
* then 和 catch 改变状态(没有报错,都返回resolved)
* then 正常返回 resolved,里面有报错则返回 rejected
* **catch 正常返回 resolved**,里面有报错则返回 rejected
![image-20210304204607634](https://image.mdashen.com/pic/image-20210304204607634.png)
![image-20210305143458087](https://image.mdashen.com/pic/image-20210305143458087.png)
#### async/await
* 产生背景
> 异步回调 callback hell ---> Promise then catch 链式调用,但也是基于回调函数 ---> **async\await是同步语法,彻底消灭回调函数**
* 立即调用函数表达式 [参考链接](https://developer.mozilla.org/zh-CN/docs/Glossary/IIFE) IIFE
```javascript
// 感叹号,用来分割上一行(上一行没分号结尾的话)
!(()=>{
console.log('')
})()
```
* async/await 只是一个语法糖
##### async/await 与promise 关系
* await 相当于 Promise then
* promise catch 可以使用 try catch 代替
* async 封装 promise 返回 promise
执行循序例题
```javascript
async function async1() {
console.log('async start')
await async2() // await 后面,都可以看做是 callback 里的内容,即异步
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
async1()
console.log('script end')
```
![image-20210305173107739](https://image.mdashen.com/pic/image-20210305173107739.png)
```javascript
async function async1() {
console.log('async start')
await async2()
console.log('async1 end')
await async3()
console.log('async1 end 2')
}
async function async2() {
console.log('async2')
}
async function async3() {
console.log('async3')
}
console.log('script start')
async1()
console.log('script end')
```
![image-20210305174406323](https://image.mdashen.com/pic/image-20210305174406323.png)
#### for ... of
* for ... in 以及 forEach for 是常规的同步遍历
* for ... of 常用于异步的遍历
```javascript
function muti(num) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(num * num)
}, 1000)
})
}
const nums = [1, 2, 3]
// 同步遍历
nums.forEach(async (i) => {
const res = await muti(i)
// 一瞬间同时打印1,4,9
console.log(res)
})
// 异步遍历
!(async () => {
for (let i of nums) {
const res = await muti(i)
// 隔一秒打印一个
console.log(res)
}
})()
```
#### 宏任务 macroTask 和微任务 microTask
```javascript
console.log('start') // 1
setTimeout(() => {
console.log('宏任务') // 4
})
Promise.resolve().then(() => {
console.log('微任务') // 3
})
console.log('end') // 2
```
* 宏任务:setTimeout seInterval Ajax DOM 事件;**DOM 渲染后触发**;微任务是 ES6 语法规定的
* 微任务:Promise async/await;**DOM 渲染前触发**;宏任务是由浏览器规定的
* **微任务执行时机比宏任务要早**
##### event-loop 和 dom 渲染的关系
![image-20210305193518694](https://image.mdashen.com/pic/image-20210305193518694.png)
* 每次 Call Stack 清空(即每次轮询结束),即同步任务执行完
* 都是 DOM 重新渲染的机会,DOM 结构如有改变则重新渲染
* 然后再去触发下一次 Event Loop
```html
<div id="container"></div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
const $p1 = $('<p>一段文字</p>')
const $p2 = $('<p>一段文字</p>')
const $p3 = $('<p>一段文字</p>')
$('#container').append($p1).append($p2).append($p3)
console.log('length', $('#container').children().length)
alert('本次 call stach 结束,DOM 结构已更新,但尚未触发渲染')
// (alert 会阻断 js 执行,也会阻断 DOM 渲染,便于查看效果)
</script>
```
##### ![image-20210305202246153](https://image.mdashen.com/pic/image-20210305202246153.png)
题目
```javascript
async function async1() {
console.log('async1 start') // 2
await async2()
console.log('async1 end') // await 后面是回调,相当于then里面,微任务 6
}
async function async2() {
console.log('async2') // 3
}
console.log('script start') // 1
setTimeout(function () { // 宏任务
console.log('setTimeout') // 8
}, 0)
async1() //
// Promise 声明中的代码会立即执行
new Promise(function (resolve) {
console.log('promise1') // 4
resolve()
}).then(function () {
console.log('promise2') // 7
})
console.log('scrtpt end') // 5 所有同步代码执行完毕、call stack清空,开始 event loop
// 同步代码 -> 微任务 -> 尝试触发DOM渲染 -> 触发 Event Loop 执行宏任务
```
![image-20210305203855500](https://image.mdashen.com/pic/image-20210305203855500.png)