Event loop 与 Async Await 比较
Node.js Event loop 与 Async Await 比较
Categories:
JS 原理
JS 为何是单线程的
最初设计JS时,主要是用来验证浏览器DOM元素的一个语言,如果js是多线程,那麽两个线程同时对一个DOM元素会进行相互冲突的操作 这样浏览器是无法执行的
JS 为何需要非同步
如果JS没有非同步,那麽程式由上而下执行,如果遇到上一行解析时间很长,那麽下面的代码就会被阻塞。对用户而言阻塞=卡死。就会有很糟的体验
JS 单线程是如何实现非同步的呢
JS 的非同步以及多线程都可以理解为一个假象。因为其实就是透过事件循环(Event Loop),去调配单一线程的执行顺序。 所以理解Event Loop 就可以理解JS非同步的运行机制。
Event Loop
为何要弄懂 Event Loop
- 掌握底层原理,可以让自己以不变应万变
- 增加深度,了解JS运行机制
Stack、Queue、Heap
堆 Heap
堆为一种数据结构,利用而二元树所获得的一种数据结构。一种为最小堆,另一种为最大堆
堆叠 Stack
Call Stack
- 一种资料结构
- 后进先出的概念
- 只能在最尾端插入后删除的线性表
Callback Queue or Event Queue
- 一种资料结构
- 先进先出的概念
- 在结构的前端进行删除,在结构的后端进行插入
- 没有元素时称为空阵列
任务种类
- 一种是宏任务(MacroTask)
- 一种是微任务(MicroTask) 不管宏任务or微任务都是进入 Event Queue
宏任务(MacroTask)
- script 全部代码
- I/O
- UI Rendeting
- MessageChannel
- postMessage
微任务(MicroTask)
- Process.nextTick(Node.js)
- Promise
- Object.observe(废弃)
- MutationObserver
queues 非同步事件
- setTimeout、setInterval、setImmediate
- click
- ajax
调用线怎麽走的
JS调用执行线为 后进先出(Stack)
的规则,当函数执行时,会被添加到线的顶部,当调用执行线执行完成后,就会从顶部移出,直到线内被清空
Event Loop 进程
- 「调用执行线」执行完同步任务后,会去看调用执行线是否为空
- 如果调用执行线为空,会去检查微任务是否为空
- 为空:执行宏任务
- 不为空:执行微任务
- 每次单个宏任务执行完毕后,检查微任务(Callback Queue)队列是否为空,不为空则依「先入先出」规则做
- 设置微任务队(Callback Queue)为 null,然后执行宏任务,如此循环
microTask 进程
- 设置 microTask 检查点标誌为true
- 当事件循环 microtask 执行不为空时:选择一个最先进入 microtask 阵列的 microtask。将事件循环 microtask 设置为以选择的 microtask,运行此 microtask,将已经执行完的 microtask 为null,移除此 microtask
- 清除 indexDB 事务
- 设置进入 microtask 检查点的标誌为 false
分析 Event Loop
- 不同类型的任务会进入到对应的Event Queue
- setTimeout or setInterval 会进入宏任务的Event Queue
- Promise or process.nextTick会进入微任务的Event Queue
- 遇到 promise.then 之类的微任务,会让这个微任务被安排在当前的宏任务中
- 当执行完一轮的同步+非同步脚本后,就往下一轮迈进
async/await
async/await 是什麽
我们创建了promise但不能同步等他执行完成。我们只能通过 then 传一个 callback function,这样很容易再次陷入 callback 地狱。 所以实际上 async/await 在底层转换了 Promise and then 的回调函数。也就是 Promise 的语法糖
- async 是非同步的缩写
- await 是等待非同步方法执行完成
async/await 用来做啥 优化 promise的回调问题
async/await 内部做了什麽
- async 返回一个 Promise 物件,如果再函数中 return 一个直接量,async 会把这个量通过 Promise.resolve() 封装成 Promise 物件。 如果你返回 Promise 就以你返回的Promise为主
- await 是等待,等待运行的返回值。await 后面通常是一个 Promise。但这不代表 await 后面只能跟非同步操作
await 等待机制
如果后面接了 Promise,就会阻塞后面的代码,等着 Promise 对象 resolve,然后得到 resolve 值作为 await 表达式的运算结果
async/await 在什麽场景下使用
如果由 多个 Promise 组成的 then 链
时,就可以使用
async/await 分析方向
- async 定义的是一个 Promise 函数,和普通函数一样只要不调用就不会进入事件阵列
- async 内部如果没有主动 return promise,那 async 会把函数的返回值用 Promise 包装
- await 必须出现在 async 函数内,await 不一定要跟一个非同步操作,也可以是一个普通表达式
- 遇到 await 关键字,await 右边的语句会立即被执行,然后 await 下面的代码进入等待状态,等待结果
- await 后面如果不是 promise对象,await 会为阻塞后面的代码,先执行 async外面的同步代码,同步执行完后,再回来 async 内部 ,把这个非 promise 的东西,作为 await 表达式的结果
- await 后面如果是 promise,await 也会暂停 async 后面的 code,先执行 async 外面的代码,等着 Promise 对象 fulfilled, 然后把 resolve 的参数作为 await 表达式的运算结果
分析原则
- 遇到 new Promise 是直接执行其任务
- new Promise 接的 then 是微任务
- await 会等到 async 内的事情都做完才往下做同个 function 内的事情
- await function 会去驱动这个 function,function 做完,再往同层的同步走下去
- async 回传 Promise & async 中的 return 会被当作 resolve value
- 下面这串算同步指令
await new Promise((res){
console.log('1')
})