JavaScript 引擎、事件循环、任务队列

Souleigh ✨ 等级 512 0 1

一、先引用别人的一幅图

(很好的概括了JS主线程和任务队列是如何执行的)

JavaScript 引擎、事件循环、任务队列

1.JS引擎和runtime的区别:

  • 引擎:解释并编译代码,让它变成能交给机器人运行的代码(runnable commands);单线程,负责维护任务队列,按照顺序把任务放入函数栈执行。
  • runtime:就是运行环境,它提供一些对外的接口供JS调用,比如,浏览器环境、Node.js环境。不同的runtime,会提供不同的接口,比如,在 Node.js 环境中,我们可以通过require来引入模块;而在浏览器中,我们有window、 DOM。图中的异步处理模块就是runtime提供的,拥有与JS引擎互不干扰的线程。
  • 总结:JS引擎可以理解为主线程,runtime可以理解为其他的线程。

二、函数栈

现在,我们要运行下面这段代码:

function bar() {
    console.log(1);
}

function foo() {
    console.log(2);
    bar();
}

setTimeout(() => {
    console.log(3)
});

foo();

它在栈中的入栈、出栈过程,如下图: JavaScript 引擎、事件循环、任务队列

总结:其实函数的入栈出栈就是当主线程调用到某个函数的时候该函数就入栈,使用JS引擎去解析执行这个函数,等到该函数执行完毕之后就会弹出函数栈。


三、任务队列

Js 中,有两类任务队列:宏任务队列(macro tasks)和微任务队列(micro tasks)。宏任务队列可以有多个,微任务队列只有一个(eg:Promise)。

  • 宏任务:script(全局任务), setTimeout, setInterval, setImmediate, I/O, UI rendering。宏任务是在下一轮的事件循环中才会执行
  • 微任务:process.nextTick, Promise, Object.observer, MutationObserver。微任务在本轮事件循环中执行
  • 执行顺序:主线程 -> 异步任务(微任务 -> 宏任务)

四、浏览器的事件循环

当函数栈为空时,就会从任务队列里面去除任务来执行。浏览器这里,分为三步:

  1. 取一个宏任务来执行。执行完毕后,下一步。(每解决一个宏任务都需要一轮的事件循环)
  2. 取一个微任务来执行,执行完毕后,再取一个微任务来执行。直到微任务队列为空,执行下一步。(微任务的执行都在同一轮的事件循环中)
  3. 更新UI渲染.

Event Loop 会无限循环执行上面3步,这就是Event Loop的主要控制逻辑。其中,第3步(更新UI渲染)会根据浏览器的逻辑,决定要不要马上执行更新。毕竟更新UI成本大,所以,一般都会比较长的时间间隔,执行一次更新。

从执行步骤来看,我们发现微任务,受到了特殊待遇!我们代码开始执行都是从script(全局任务)开始,所以,一旦我们的全局任务(属于宏任务)执行完,就马上执行完整个微任务队列。

看个例子:

console.log('script start');

// 微任务
Promise.resolve().then(() => {
    console.log('p 1');
});

// 宏任务
setTimeout(() => {
    console.log('setTimeout');
}, 0);

var s = new Date();
while(new Date() - s < 50); // 阻塞50ms

// 微任务
Promise.resolve().then(() => {
    console.log('p 2');
});

console.log('script end');

输出如下:

/*** output ***/

// one macro task
script start
script end

// all micro tasks
p 1
p 2

// one macro task again
setTimeout

上面之所以加50ms的阻塞,是因为setTimeoutdelayTime 最少是 4ms. 为了避免认为setTimeout是因为4ms的延迟而后面才被执行的,我们加了50ms阻塞。


五、NodeJs 的 事件循环

NodeJs 的运行是这样的:

  • 初始化事件循环
  • 执行你的主代码。这里同样,遇到异步处理,就会分配给对应的队列。知道主代码执行完毕。
  • 执行主代码中出现的所有微任务:先执行完所有nextTick(),然后在执行其它所有微任务。
  • 开始 Event Loop
收藏
评论区

相关推荐

JavaScript 引擎、事件循环、任务队列
一、先引用别人的一幅图 (很好的概括了JS主线程和任务队列是如何执行的) 1.JS引擎和runtime的区别: 引擎:解释并编译代码,让它变成
What the f*ck JavaScript?
What the fck JavaScript? 一个有趣和棘手的 JavaScript 示例列表。 JavaScript 是一种很好的语言。
昨天写了这些骚代码,今天上班差点被同事揍了
昨天写了这些骚代码,今天上班差点被同事揍了 前端开发 微信号 qianduan1024 功能介绍 专注于Web前端技术文章分享,包含JavaScript、HTML5、CSS3等前端基础知识,以及Vue.js,React,Augular等前端框架 收录于话题 来自:掘金,作者:布拉德特皮 链接:h
【读vue 源码】溯源 import Vue from 到底做了什么?
阅读资源 vue.js源码托管地址(https://links.jianshu.com/go?tohttps%3A%2F%2Fgithub.com%2Fvuejs%2Fvue) flow 静态检查工具地址(https://links.jianshu.com/go?tohttps%3A%2F%2Fflow.org%2Fen%2Fdoc
微信小程序支付功能全流程实践
前言 微信小程序为电商类小程序,提供了非常完善、优秀、安全的支付功能。在小程序内可调用微信的API完成支付功能,方便、快捷。小程序开发者在开发小程序时,支付流程是必然要接触到,今天胡哥就小程序支付的全流程为大家一一细说,让小伙伴能快速得掌握小程序支付能力,避免踩坑! 知己知彼,方能百战不殆 小程序支付流程图 小程序支付交互流程图(https:/
对 JavaScript 中事件循环的理解​
一、是什么 JavaScript 在设计之初便是单线程,即指程序运行时,只有一个线程存在,同一时间只能做一件事 为什么要这么设计,跟JavaScript的应用场景有关 JavaScript 初期作为一门浏览器脚本语言,通常用于操作 DOM ,如果是多线程,一个线程进行了删除 DOM ,另一个添加 DOM,此时浏览器该如何处理? 为了解决单
微信小程序部分api 会触发 onShow onHide
解决部分api触发小程序 onShow onHide 首先要明白 微信小程序的 onShow() onHide()分为页面级的和应用级的,应用级的就是app.js里面的那几个,页面级的就是pages里的 当使用了下列api时,均会触发页面级和应用级的onShow onHide 1. 点击右上角小圆点关闭小程序。 2. 图片预览:wx.preview
巨大提升!更快的 async 函数和 promises
(https://imghelloworld.osscnbeijing.aliyuncs.com/669a1c8f7203559afa4621628303674c.png) 翻译自:Faster async functions and promises(https://v8.dev/blog/fastasync) JavaScript
如果有人再问你 Java 的反射,把这篇文章扔给他
在 Java 中,并不是所有的类型信息都能在编译阶段明确,有一些类型信息需要在运行时才能确定,这种机制被称为 RTTI,英文全称为 RunTime Type Identification,即运行时类型识别,有没有一点“知行合一”的味道?运行时类型识别主要由Class类实现。 在日常的学习工作当中,有一些知识是我们在读书的时候就能够习得;但有一些知识不是的
使用 VS Code 来开发和调试 Python 程序
(简称 VSCode)是微软出品的一款支持多种语言的免费 IDE(集成开发环境)。VSCode 轻量而强大,支持 Windows、macOS 和 Linux。内置支持 JavaScript、TypeScript 和 Node.js,并且拥有一个丰富的插件生态系统来支持其它语言(C/C、C、Java、Python、PHP、Go 等)和运行时(.Net 和
Flutter - 深入理解setState更新机制
基于Flutter 1.5的源码剖析, 分析flutter的StatefulWidget的UI更新机制,相关源码:widgets/framework.dartwidgets/binding.dartscheduler/binding.dartlib/ui/window.dartflutter/runtime/runtime_controller.c
Dart虚拟机运行原理
一、Dart虚拟机 1.1 引言Dart VM是一种虚拟机,为高级编程语言Dart提供执行环境,但这并意味着Dart在D虚拟机上执行时,总是采用解释执行或者JIT编译。 例如还可以使用Dart虚拟机的AOT管道将Dart代码编译为机器代码,然后运行在Dart虚拟机的精简版环境,称之为预编译运行时(precompiled runtime)环境,该环境不包含任何
解读Dart虚拟机的参数列表
一、概述在third\party/dart/runtime/vm/flag\list.h定义了Dart虚拟机中所有标志的列表,标志分为以下几大类别: Product标记:可以在任何部署模式中设置,包括Product模式 Release标志:通常可用的标志,除Product模式以外 Precompile标志:通常可用的标志,除Product模式或已
Webpack 热更新以及原理
什么是热更新模块热替换(hot module replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新所有类型的模块,而无需完全刷新一般的刷新我们分两种: 一种是页面刷新,不保留页面状态,就是简单粗暴,直接 window.location.reload()。 另一种是基于 WDS (Webpackde
JavaScript 和 Node.js 中事件循环
1.JavaScript中事件循环可以参考《JavaScript忍者秘籍第二版》第十三章,讲解的很好。JavaScript中事件循环,主要就在理解宏任务和微任务这两种异步任务。宏任务(macrotask): setTimeOut 、 setInterval 、 setImmediate 、 I/O 、 各种callback、 UI渲染 、messageCh