最通俗易懂的JS事件循环机制

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

既然我们要说JS的事件循环,那我们就有必要知道任务队列这个概念,这个任务队列它是有同步任务与异步任务的。

同步任务就是当浏览器第一遍过滤html文件的时候可以执行完的就是同步任务,异步任务就是比较耗费时间与性能的,当浏览器执行到这些的时候会将其丢到异步任务队列中,异步任务有分宏任务与微任务

列举几个常见的

宏任务:script脚本、DOM事件、定时器setTimeout、AJAX请求、I/O流等等...

微任务:Promise函数、async await等等...

我们先看一张图方便我们理解一下他们的执行顺序还有什么是宏任务什么是微任务

这里简单解答一下,就是当js执行的时候将所有任务丢到任务区,然后经过任务区识别并进行分拣处理,当遇到比如定时器或者点击事件这类需要消耗大量时间的任务,会存到任务队列,然后划分为宏任务,并将他们细分到微任务解决,也就是让这些复杂的逻辑处理放到宏任务队列之后,让他们自己内部消化处理。

然后,一个宏任务和它下方的所有微任务合在一起统称事件循环。

但是一个程序肯定不是全都是事件循环,比如有些立即执行的函数或者任务,会穿插在事件队列之中。所以就是有一个现象就是事件阻塞,简单说就是因为js是单线程的,所以当遇到一些复杂的逻辑处理,会消耗大量时间去计算,但又不可以挡住后面的正在排队的立即执行的,所以才会调到任务队列中。

我们来看一个demo,方便理解一下

console.log("1");

setTimeout( ()=>{
    console.log("2");
},0 )

Promise.resolve().then(()=>{
    console.log("3")
})

console.log("4");

我们来看一下打印顺序

打印的时候为什么会这样,因为立即执行的任务会马上执行掉,然后遇到宏任务会丢到事件队列中进行不断地事件循环。这就是它的执行机制了,根据我们的了解,微任务应该是跟在宏任务后面的,为什么Promise会比定时器先执行呢?

这里是因为定时器和Promise之间是有一个DOM渲染的。我们来改造一下这个demo测试一下

结构只有一个div标签

<body>
  <div id="box"></div>
  
  <script src="./app.js"></script>s
</body>
const box = document.getElementById('box')
box.innerHTML = '<P>我是后来插进去的内容</P>'

console.log("1");

setTimeout( ()=>{
    console.log("2");
    alert("定时器执行了")
},0 )


Promise.resolve().then(()=>{
    console.log("3")
    alert("Promise执行了")
})


console.log("4");

我们看一下浏览器,当我们打开的第一时间

它会先执行这,然后我再点击确定

这样我们就得知Promise是在DOM渲染之前的,所以就知道为什么Promise先执行。

然后我们现在得出一个结论就是,微任务是先于DOM渲染,然后才到宏任务的。但是内部执行是微任务先于宏任务的

总结:

1,异步任务分类:宏任务(setTimeout),微任务(promise)

2,遇到异步任务放到任务表中,等事件执行完成(ajax请求完成、setTimeout设置时间到期),之后放入到任务队列

3,当执行栈的同步任务执行完成之后,就会执行任务队列的第一个异步任务,其中把宏观任务和微观任务都执行完成后才进行下一次循环

猜你喜欢

转载自juejin.im/post/7114940689879662600