美文网首页
从Event Loop谈JS的运行机制 - 2023-02-15

从Event Loop谈JS的运行机制 - 2023-02-15

作者: 勇敢的小拽马 | 来源:发表于2023-02-14 10:35 被阅读0次
  • JS分为同步任务和异步任务
  • 同步任务都在主线程上执行,形成一个执行栈
  • 主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
  • 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。
image.png
  • 主线程运行时会产生执行栈,栈中的代码调用某些api时,它们会在事件队列中添加各种事件(当满足触发条件后,如ajax请求完毕)

  • 而栈中的代码执行完毕,就会读取事件队列中的事件,去执行那些回调

  • 如此循环

  • 注意,总是要等待栈中的代码执行完毕后才会去读取事件队列中的事件

定时器线程

上述事件循环机制的核心是:JS引擎线程事件触发线程

但事件上,里面还有一些隐藏细节,譬如调用setTimeout后,是如何等待特定时间后才添加到事件队列中的?

是JS引擎检测的么?当然不是了。它是由定时器线程控制(因为JS引擎自己都忙不过来,根本无暇分身)

为什么要单独的定时器线程?因为JavaScript引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确,因此很有必要单独开一个线程用来计时。

什么时候会用到定时器线程?当使用setTimeout或setInterval时,它需要定时器线程计时,计时完成后就会将特定的事件推入事件队列中。

W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。

setTimeout而不是setInterval

用setTimeout模拟定期计时和直接用setInterval是有区别的。

因为每次setTimeout计时到后就会去执行,然后执行一段时间后才会继续setTimeout,中间就多了误差
(误差多少与代码执行时间有关)

而setInterval则是每次都精确的隔一段时间推入一个事件
(但是,事件的实际执行时间不一定就准确,还有可能是这个事件还没执行完毕,下一个事件就来了)

而且setInterval有一些比较致命的问题就是:

  • 累计效应(上面提到的),如果setInterval代码在(setInterval)再次添加到队列之前还没有完成执行,就会导致定时器代码连续运行好几次,而之间没有间隔。
    就算正常间隔执行,多个setInterval的代码执行时间可能会比预期小(因为代码执行需要一定时间)
  • 譬如像iOS的webview,或者Safari等浏览器中都有一个特点,在滚动的时候是不执行JS的,如果使用了setInterval,会发现在滚动结束后会执行多次由于滚动不执行JS积攒回调,如果回调执行时间过长,就会非常容器造成卡顿问题和一些不可知的错误

所以,鉴于这么多但问题,目前一般认为的最佳方案是:用setTimeout模拟setInterval,或者特殊场合直接用requestAnimationFrame

进阶:macrotask与microtask

JS中分为两种任务类型:macrotaskmicrotask,在ECMAScript中,microtask称为jobs,macrotask可称为task

  • macrotask:主代码块,setTimeout,setInterval等(可以看到,事件队列中的每一个事件都是一个macrotask)
    0 microtask:Promise,process.nextTick等

补充:在node环境下,process.nextTick的优先级高于Promise__,也就是可以简单理解为:在宏任务结束后会先执行微任务队列中的nextTickQueue部分,然后才会执行微任务中的Promise部分。

再根据线程来理解下:

  • macrotask中的事件都是放在一个事件队列中的,而这个队列由事件触发线程维护
  • microtask中的所有微任务都是添加到微任务队列(Job Queues)中,等待当前macrotask执行完毕后执行,而这个队列由JS引擎线程维护

使用MutationObserver实现microtask

MutationObserver可以用来实现microtask
(它属于microtask,优先级小于Promise,
一般是Promise不支持时才会这样做)

它是HTML5中的新特性,作用是:监听一个DOM变动,
当DOM对象树发生任何变动时,Mutation Observer会得到通知

像以前的Vue源码中就是利用它来模拟nextTick的,
具体原理是,创建一个TextNode并监听内容变化,
然后要nextTick的时候去改一下这个节点的文本内容,
如下:(Vue的源码,未修改)

var counter = 1
var observer = new MutationObserver(nextTickHandler)
var textNode = document.createTextNode(String(counter))

observer.observe(textNode, {
    characterData: true
})
timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
}

不过,现在的Vue(2.5+)的nextTick实现移除了MutationObserver的方式(据说是兼容性原因),
取而代之的是使用MessageChannel
(当然,默认情况仍然是Promise,不支持才兼容的)。

MessageChannel属于宏任务,优先级是:MessageChannel->setTimeout
所以Vue(2.5+)内部的nextTick与2.4及之前的实现是不一样的,需要注意下。

这里不展开,可以看下https://juejin.im/post/5a1af88f5188254a701ec230

相关文章

  • 从Event Loop谈JS的运行机制 - 2023-02-15

    JS分为同步任务和异步任务 同步任务都在主线程上执行,形成一个执行栈 主线程之外,事件触发线程管理着一个任务队列,...

  • 浅析Vue.nextTick()原理

    1、为什么用Vue.nextTick() 首先来了解一下JS的运行机制。 JS运行机制(Event Loop) J...

  • Js 的多宿主时代

    之前有写过一篇文章 Js运行机制深层剖析,主要讲的是 Js 的事件循环机制 (Event Loop),从现在的眼光...

  • JavaScript 运行机制 Event loop

    JavaScript Event Loop 今天要分享的是javascript事件的运行机制,我们知道js是单线程...

  • Event Loop

    JS 主线程不断的循环往复的从任务队列中读取任务,执行任务,这种运行机制称为事件循环(event loop)推荐看...

  • JS运行机制到Node.js 事件循环

    参考文章:阮一峰博客:JavaScript 运行机制详解:再谈Event Loop 1. JS 为什么是单线程? ...

  • js引擎的执行机制

    js引擎的执行机制 JS的Event Loop是JS的执行机制,理解JS的执行,必须理解Event Loop JS...

  • JavaScript-事件循环

    参考JavaScript运行机制之事件循环(Event Loop)详解 从setTimeout说事件循环模型 单...

  • Node—关于Event Loop的学习笔记

    一、什么是Event Loop Event Loop指的是计算机系统的一种运行机制,在JavaScript中就是采...

  • 夯基础- js event loop机制

    js运行机制 event loop事件循环 js分为同步任务和异步任务,所有的同步任务都在主线程上执行 另外存在着...

网友评论

      本文标题:从Event Loop谈JS的运行机制 - 2023-02-15

      本文链接:https://www.haomeiwen.com/subject/vjfrkdtx.html