Event Loop,简单翻译就是 事件循环,是 JS 语言下实现运行时的一个机制。

JS 的异步并不像其他语言(比如 Java)的异步那样可以实现真正的并发执行,本身其实是个单线程。

JS 是维护了一个 任务队列,每当要执行一些异步任务,比如定时器或者是点击按钮触发的事件响应函数。它们不会立即执行,而是放到这个队列里,等待已经在排队的其他任务先执行完,才轮到它们。

队列是一个操作受限的有序列表,表现为为先进入的元素必须先出去,即 “先进先出”,很像排队的感觉。

不过也有一些特殊的队列,比如优先级队列,它是优先级高的元素先出队。

之所以叫 Event Loop,因为它的逻辑可以描述为下面代码:​

while (queue.waitForMessage()) {
  queue.processNextMessage();
}

当一个任务被完成后,队列会变成等待下一个任务状态,然后处理下一个任务,如此循环往复。

因为 JS 的代码执行本身是一个单线程,为了不让执行阻塞,JS 会把网络请求操作、渲染浏览器页面等操作,交给其他的线程,等待其他线程处理好把结果返回给 JS。

所以 JS 不合适 CPU 密集型,更适合 IO 密集型的场景。因为它只有一个线程,如果计算耗时太长,就会阻塞其他要执行的任务,导致卡顿,甚至崩溃。

setTimeout 定时器并不准

setTimeout 在特定时间后要执行的函数,并不会立即执行,而是会先放到任务队列中,先等待前面的任务同步执行完成了,才能执行我们这个。

下面看一个例子,因为第一个 setTimout 有一个非常耗时的同步任务,导致下一个 setTimeout 的执行阻塞,比前面一个 setTimeout 执行要慢半秒。

const start = new Date().getTime();
setTimeout(() => {
  console.log('1:', new Date().getTime() - start);
  let num = 0;
  for (let i = 0; i < 999999999; i  ) {
    num = i;
  }
}, 1000);
setTimeout(() => {
  console.log('2:', new Date().getTime() - start);
}, 1000);
/**
 * 输出结果:
 * 1: 1001
 * 2: 1505
 */

定时器的时间,指的是能执行的最早时间,但不能保证一定能在这个时间点立即执行。

宏任务和微任务

任务队列并不是严格意义上的先进先出的正常队列,是可以调整执行顺序的。

我们将要执行的任务分为宏任务和微任务,其中宏任务是正常的先进先出,而微任务则是可以插队,优先于宏任务先执行。宏任务必须在所有微任务执行后才能执行。

当我们给任务队列添加一个微任务时,它会跑到任务队列宏任务前。多个微任务入队时,会保持它们的相对顺序。

宏任务有:

  • script,即 HTML 嵌入的脚本。
  • setTimeout / setInterval 定时器。
  • setImmediate,这是 nodejs 特有的 API。
  • requestAnimationFrame,会在页面重绘前执行。
  • I/O 操作,比如网络请求完成的回调函数执行任务、还比如点击按钮要执行的回调等。这些操作其实是其他的线程完成后触发的,暂且归纳为 I/O 操作。

微任务有:

  • Promise 从 pending 状态转换为其他状态时,触发 then/catch/finaly 中的函数,比如  Promise.resolve().then(fn) 。这是最常见的微任务。
  • MutationObserver,用于监听 DOM 的变化。
  • process.nextTick,nodejs 特有的 API。

任务队列,理论上一个就够了,但也可以是多个队列的组合,没有强行要求。

多个任务队列的实现可以更好地实现优先级的控制,比如对于定时器任务,理论上应该是在多个宏任务中最先执行比较好。浏览器没考虑这种情况,但 nodejs 给宏任务中也设置优先级,会让定时器任务最先执行。

Event Loop 还是挺复杂的,标准文档也比较长,我也没怎么看,感兴趣可以看看。

https://html.spec.whatwg.org/multipage/webappapis.html#event-loops。

一道经典异步题

async function async1() {
  console.log("async1 start");
  await async2();
  console.log("async1 end");
}
async function async2() {
  console.log("async2");
}
console.log("script start");
setTimeout(function() {
  console.log("setTimeout");
}, 0);
async1();
new Promise(function(resolve) {
  console.log("promise1");
  resolve();
}).then(function() {
  console.log("promise2");
});
console.log('script end')

解题思路为:

  • 找到同步代码。同步代码有:普通同步代码、new Promise(fn) 执行传入的回调函数、async 执行时遇到 await 的前面部分(包括  await 的右侧函数执行也是同步的,这里是易错点 )。
  • 看看任务队列中有哪些微任务和宏任务,记住微任务全执行完了才会执行宏任务。
  • 执行任务,任务里面的异步任务又按顺序进入到任务队列。

结果是:

// 同步代码
script start
async1 start
async2
promise1
script end
// 微任务
async1 end
promise2
// 宏任务
setTimeout

结尾

JS 运行机制是单线程,当有多个异步任务要同时执行,并不能并发执行,必须让优先级高的任务执行完才能执行后面的。如果正在执行的任务比较耗时,会导致后面的任务被阻塞。

Event Loop 的机制中,最基本的一条就是:微任务比宏任务先执行。

到此这篇关于JS 中的 Event Loop 是什么?的文章就介绍到这了,更多相关JS 中的 Event Loop内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

JS 中的 Event Loop 是什么你真的懂的更多相关文章

  1. html5 拖拽及用 js 实现拖拽功能的示例代码

    这篇文章主要介绍了html5 拖拽及用 js 实现拖拽,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  2. 详解使用postMessage解决iframe跨域通信问题

    这篇文章主要介绍了详解使用postMessage解决iframe跨域通信问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5数字输入仅接受整数的实现代码

    这篇文章主要介绍了HTML5数字输入仅接受整数的实现代码,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  4. amaze ui 的使用详细教程

    这篇文章主要介绍了amaze ui 的使用详细教程,本文通过多种方法给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. HTML5手指下滑弹出负一屏阻止移动端浏览器内置下拉刷新功能的实现代码

    这篇文章主要介绍了HTML5手指下滑弹出负一屏阻止移动端浏览器内置下拉刷新功能的实现代码,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  6. 浅谈html5之sse服务器发送事件EventSource介绍

    本篇文章主要介绍了浅谈html5之sse服务器发送事件EventSource介绍,具有一定的参考价值,有兴趣的可以了解一下

  7. HTML5 拖放(Drag 和 Drop)详解与实例代码

    本篇文章主要介绍了HTML5 拖放(Drag 和 Drop)详解与实例代码,具有一定的参考价值,有兴趣的可以了解一下

  8. HTML中实现音乐或视频自动播放案例详解

    由于期末大作业我想插入一个背景音乐,实现点开网页就会自动播放音频的效果,今天通过本文给大家分享下我基于HTML实现音乐或视频自动播放功能,代码简单易懂,需要的朋友参考下吧

  9. ios – Swift中的非响应流委托

    所以我在Swift中使用套接字并试图将应用程序与我的服务器连接起来.我让应用程序连接到服务器的IP地址,并在服务器上使用netcat进行测试.在执行期间,应用程序的控制台输出显示它已成功连接到服务器.但是,流委托似乎没有响应.当我输入netcat时,app控制台没有打印任何内容.我已经搜索了很长一段时间,发现我的实现与其他实现非常相似.也许我在这里遗漏了一些我看不到的东西.任何想到这个问题的人都将不胜感激!

  10. ios – UIScrollView内容不允许用户交互

    我有一个启用了分页的UIScrollView,如下所示:在UIScrollView中,我添加了几个UIWebViews,并将其启用的交互设置为是这样的.它打破了UIScrollView上的分页和所有触摸.如果我将用户交互设置为NO,则页面有效,但我无法在UIWebView中突出显示文本.我试着像下面那样对UIScrollView进行子类化,但是会出现同样的情况.任何的想法?

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部