ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
### 异步基础 #### 单线程和异步 * 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)