前言

众所周知,为了与浏览器进行交互,Javascript是一门非阻塞单线程脚本语言。
  1. 为何单线程? 因为如果在DOM操作中,有两个线程一个添加节点,一个删除节点,浏览器并不知道以哪个为准,所以只能选择一个主线程来执行代码,以防止冲突。虽然如今添加了webworker等新技术,但其依然只是主线程的子线程,并不能执行诸如I/O类的操作。长期来看,JS将一直是单线程。

  2. 为何非阻塞?因为单线程意味着任务需要排队,任务按顺序执行,如果一个任务很耗时,下一个任务不得不等待。所以为了避免这种阻塞,我们需要一种非阻塞机制。这种非阻塞机制是一种异步机制,即需要等待的任务不会阻塞主执行栈中同步任务的执行。这种机制是如下运行的:

    • 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)
    • 等待任务的回调结果进入一种任务队列(task queue)
    • 当主执行栈中的同步任务执行完毕后才会读取任务队列,任务队列中的异步任务(即之前等待任务的回调结果)会塞入主执行栈,
    • 异步任务执行完毕后会再次进入下一个循环。此即为今天文章的主角事件循环(Event Loop)

    用一张图展示这个过程:

     
    Markdown

正文

1.macro task与micro task

在实际情况中,上述的任务队列(task queue)中的异步任务分为两种:微任务(micro task)宏任务(macro task)

  • micro task事件:Promises(浏览器实现的原生Promise)MutationObserverprocess.nextTick
    <br />
  • macro task事件:setTimeoutsetIntervalsetImmediateI/OUI rendering
    这里注意:script(整体代码)即一开始在主执行栈中的同步代码本质上也属于macrotask,属于第一个执行的task

microtask和macotask执行规则:

  • macrotask按顺序执行,浏览器的ui绘制会插在每个macrotask之间
  • microtask按顺序执行,会在如下情况下执行:
    • 每个callback之后,只要没有其他的JS在主执行栈中
    • 每个macrotask结束时

下面来个简单例子:

console.log(1);

setTimeout(function() {
console.log(2);
}, 0); new Promise(function(resolve,reject){
console.log(3)
resolve()
}).then(function() {
console.log(4);
}).then(function() {
console.log(5);
}); console.log(6);

一步一步分析如下:

  • 1.同步代码作为第一个macrotask,按顺序输出:1 3 6
  • 2.microtask按顺序执行:4 5
  • 3.microtask清空后执行下一个macrotask:2

再来一个复杂的例子:

// Let's get hold of those elements
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner'); // Let's listen for attribute changes on the
// outer element
new MutationObserver(function() {
console.log('mutate');
}).observe(outer, {
attributes: true
}); // Here's a click listener…
function onClick() {
console.log('click'); setTimeout(function() {
console.log('timeout');
}, 0); Promise.resolve().then(function() {
console.log('promise');
}); outer.setAttribute('data-random', Math.random());
} // …which we'll attach to both elements
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);

假设我们创建一个有里外两部分的正方形盒子,里外都绑定了点击事件,此时点击内部,代码会如何执行?一步一步分析如下:

  • 1.触发内部click事件,同步输出:click
  • 2.将setTimeout回调结果放入macrotask队列
  • 3.将promise回调结果放入microtask
  • 4.将Mutation observers放入microtask队列,主执行栈中onclick事件结束,主执行栈清空
  • 5.依序执行microtask队列中任务,输出:promise mutate
  • 6.注意此时事件冒泡,外部元素再次触发onclick回调,所以按照前5步再次输出:click promise mutate(我们可以注意到事件冒泡甚至会在microtask中的任务执行之后,microtask优先级非常高)
  • 7.macrotask中第一个任务执行完毕,依次执行macrotask中剩下的任务输出:timeout timeout

JS事件循环(Event Loop)机制的更多相关文章

  1. JavaScript事件循环(Event Loop)机制

    JavaScript 是单线程单并发语言 什么是单线程 主程序只有一个线程,即同一时间片断内其只能执行单个任务. 为什么选择单线程? JavaScript的主要用途是与用户互动,以及操作DOM.这决定 ...

  2. 事件循环 event loop 究竟是什么

    事件循环 event loop 究竟是什么 一些概念 浏览器运行时是多进程,从任务管理器或者活动监视器上可以验证. 打开新标签页和增加一个插件都会增加一个进程,如下图:  浏览器渲染进程是多线程,包 ...

  3. 事件循环Event loop到底是什么

    摘要:本文通过结合官方文档MDN和其他博客深入解析浏览器的事件循环机制,而NodeJS有另一套事件循环机制,不在本文讨论范围中.process.nextTick和setImmediate是NodeJS ...

  4. 简单了解一下事件循环(Event Loop)

    关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...

  5. 浏览器与Node的事件循环(Event Loop)有何区别?

    前言 本文我们将会介绍 JS 实现异步的原理,并且了解了在浏览器和 Node 中 Event Loop 其实是不相同的. 一.线程与进程 1. 概念 我们经常说 JS 是单线程执行的,指的是一个进程里 ...

  6. JavaScript 事件循环 — event loop

    引言 相信所有学过 JavaScript 都知道它是一门单线程的语言,这也就意味着 JS 无法进行多线程编程,但是 JS 当中却有着无处不在的异步概念 .在初期许多人会把异步理解成类似多线程的编程模式 ...

  7. JavaScipt 中的事件循环(event loop),以及微任务 和宏任务的概念

    说事件循环(event loop)之前先要搞清楚几个问题. 1. js为什么是单线程的? 试想一下,如果js不是单线程的,同时有两个方法作用dom,一个删除,一个修改,那么这时候浏览器该听谁的?   ...

  8. 一文梳理JavaScript 事件循环(Event Loop)

    事件循环(Event Loop),是每个JS开发者都会接触到的概念,但是刚接触时可能会存在各种疑惑. 众所周知,JS是单线程的,即同一时间只能运行一个任务.一般情况下这不会引发问题,但是如果我们有一个 ...

  9. 事件循环Event Loop

    在 事件循环 期间的某个时刻,运行时会从最先进入队列的消息开始处理队列中的消息.被处理的消息会被移出队列,并作为输入参数来调用与之关联的函数.正如前面所提到的,调用一个函数总是会为其创造一个新的栈帧. ...

随机推荐

  1. Mysql --09 Innodb核心特性——事务

    目录 Innodb核心特性--事务 1.什么是事务 2.事务的通俗理解 3.事务ACID特性 4.事务流程举例 5.事务的控制语句 6.事务隐式提交情况 7.事务日志redo基本功能 8.redo数据 ...

  2. Linux学习之旅(二)Linux文档操作

    目录操作 1. 创建目录 // 目录可以是绝对路径,也可以是相对路径 mkdir 目录名 //创建一个目录 mkdir -p 目录名1/目录名1/... //一次性创建多级目录 2. 删除目录 // ...

  3. go语言从例子开始之Example31.定时器

    我们常常需要在后面一个时刻运行 Go 代码,或者在某段时间间隔内重复运行. Go 的内置 定时器 和 打点器 特性让这些很容易实现.我们将先学习定时器,然后再学习打点器. Example: packa ...

  4. 读书笔记一、pandas数据结构介绍

    pandas数据结构介绍 主要两种数据结构:Series和DataFrame.   Series   Series是一种类似于一维数组的对象,由一组数据(各种NumPy数据类型)+数据标签(即索引)组 ...

  5. 牛客小白月赛16 F 小石的妹子 (线段树)

    链接:https://ac.nowcoder.com/acm/contest/949/F来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言52428 ...

  6. 编程题: 写一个 Singleton

    Singleton 模式主要作用是保证在 Java 应用程序中,一个类 Class 只有一个实例存在.举例:定义 一个类,它的构造函数为 private 的,它有一个 static 的 private ...

  7. 通过ssh访问虚拟机中的ubuntu系统

    首先把 network 连接方式由 NAT 改为 Bridge Adapter,这样虚拟机中的 ubuntu 就可以有独立的 IP 地址. 安装 openssh: sudo apt-get insta ...

  8. Vue.js----date与时间戳的转换(unixTime)Moment.js让日期处理变得更简单

    当前日期格式化 let curTime = moment().format('YYYY-MM-DD HH:mm:ss') console.log('当前日期时间curTime:' + curTime) ...

  9. js DOM0级事件和DOM2级事件

    注册事件有两种方式,分别是DOM0级和DOM2级 DOM0级就是通过事件绑定的形式dom元素只能有(绑定)一个事件处理函数,他的特点是同一个元素绑定相同事件, 后面函数会覆盖前面的 绑定: dom.o ...

  10. 超微主板IPMI的使用

    https://blog.nicky1605.com/supermicro-motherboards-use-ipmi.html IPMI(智能平台管理接口)现在大部分都是集成到主板上了,我们利用IP ...