事件循环(Event Loop)机制
Posted 前端学堂
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了事件循环(Event Loop)机制相关的知识,希望对你有一定的参考价值。
由于javascript是一门单线程的非阻塞的脚本语言。
单线程意味着JavaScript代码在执行的任何时候,都只有一个主线程来处理所有的任务。
而非阻塞则是当代码需要进行一项异步任务(无法立刻返回结果,需要花一定时间才能返回的任务,如I/O事件,网络请求)的时候,主线程会挂起(pending)这个任务,然后在异步任务返回结果的时候如果主线程空闲,则会去执行相应的回调。
1. JavaScript是单线程,非阻塞的
单线程:DOM渲染和执行JavaScript代码时,都只有一个主线程来处理所有的任务。
非阻塞: 当执行一项异步任务(无法立即返回结果,需要花费一定的时间,如AJAX请求, I/O事件)时,主线程会挂起(pending)这个任务,然后在异步返回结果的时候再根据一定的规则去执行相应的回调。
2. 执行栈和事件队列
当javascript代码执行的时候会将不同的变量存于内存中的不同位置:堆(heap)和栈(stack)中来加以区分。其中,堆里存放着一些对象。而栈中则存放着一些基础类型变量以及对象的指针。
执行栈:JS代码执行时,全局代码会创建一个全局执行环境(什么是执行环境),函数被调用时会创建一个函数执行环境。由于JS是单线程,一次只能执行一个函数,于是这些函数在执行时就会按照先进后出的顺序添加到执行栈中。
事件队列:异步代码执行时,不会等待它返回结果,而是将这个事件挂起,继续执行执行栈中的其他任务。当异步事件返回结果时,JS会将这个事件加入与当前执行栈不同的另外一个队列,即事件队列中。事件队列中的回调函数不会立即执行,而是等待当前执行栈中所有的任务执行完毕后,主线程处于空闲状态时,再执行回调函数中的同步代码。如此反复的过程,就是事件循环。
宏任务(macrotast)和微任务(microtask)
根据异步事件的类型,这个事件会被放到宏任务或微任务中去。在当前执行栈为空时,主线程会查看微任务队列中是否有事件。
如果有,则依次执行全部微任务,如果微任务中产生了新的微任务,则继续执行微任务,直到所有微任务都执行完毕。然后再去宏任务队列中取出最前面的事件,然后执行其回调函数。
如果没有,则执行宏任务中的事件
当前执行栈执行完毕时,然后会开始下一轮的事件循环,即执行微任务队列中的全部事件,然后再执行下一个宏任务队列中的事件。
宏任务有:
script中的同步代码
setTimeout
setInterval
requestAnimationFrame
setImmediate(Node.js中)
UI Render
微任务有:
Promise
MutationObserve
Process.nextTick(Node.js中)
3. 实现一个函数,接收一个函数作为参数,然后反序执行
逻辑:在同步代码执行完后,主线程空闲状态,然后在执行异步回调。
let timer = null
const tasks = []
function fn(callback) {
if (timer) {
clearTimeout(timer)
}
tasks.unshift(callback)
timer = setTimeout(() => tasks.forEach(cb => cb()))
}
fn(() => console.log(1))
fn(() => console.log(2))
fn(() => console.log(3))
// 打印结果 321
及时获取更新,了解更多动态,请关注 https://www.gogoing.site
以上是关于事件循环(Event Loop)机制的主要内容,如果未能解决你的问题,请参考以下文章
前端基础 | JS异步执行机制——事件循环(Event Loop)
第45期详解JavaScript中的Event Loop(事件循环)机制