浅谈javascript事件环微任务和宏任务队列原理

JS 事件环

JS 程序的运行是离不开事件环机制的,这个机制保证在发生某些事情的时候我们有机会执行一个我们事先预定好的函数,事情发生的时候 JS 会将相应的函数入栈执行然后出栈,但是关于事件环我们还有一些未知的东西,例如,setTimeout 我们习惯称他为定时器,但是可能很多人没有意识到,这个东西和我们常用的一些事件没什么不同,只不过我们通常所说的事件大多需要用户触发,而 setTimeout 不用用户自己触发,而是指定时间之后触发;那么问题来了,如果我们将时间设置为 0 会发生什么?会立即执行么?

setTimeout、DOM或者 HTTP请求这部分其实并不在 v8 引擎中,这些属于 web API,javascript 是一个单线程的语言,也就意味着一次只能做一件事情,这个事实从未改变

执行原理

JS 中所有的方法都会被推入栈中执行,执行完成被弹出,在遇到异步代码的时候,例如 setTimeout MutationObserver Promise 异步的部分会由其他掌管 webAPI 的地方执行,等异步有结果之后,回调函数会进入相应的队列,Promise MutationObserver 回调进入微任务队列,setTimeout setInterval requestAnimationFrame 进入宏任务队列。等待主线程的执行栈空了,微任务队列立刻被推入栈中执行,执行完毕开始执行宏任务队列

一个经典的例子

html

<div class="outer">
 <div class="inner"></div>
</div>

js

// 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);

以上代码在,手动点击 inner 元素的时候会有如下输出

click
promise
mutate
click
promise
mutate
timeout
timeout

截止 2020年8月份 chrome edge opera firefox 的结果是统一的,但是在此之前的版本可能会有不同的输出。

一个奇怪的现象

上述代码我们不使用手动触发点击,而是使用 inner.click() 触发点击,其结果会有很大的不同

click
click
promise
mutate
promise
timeout
timeout

造成以上巨大差异的原因是,手动点击,不是通过函数进入执行栈的方式触发点击事件的回调,所以inner 的回调执行完了主线程中的执行栈就是空的可以直接执行队列中任务,然后事件冒泡导致的回调函数才被推入栈运行;而 click 方法的点击则是通过将 click 推入栈中执行来达到的,inner 的点击回调执行完了之后 click 方法并没有被弹出栈,而是直接执行冒泡的下一个回调,由于下一个回调有一个重复的 属性设置 这是不会重复触发 MutationObserver的所以 mutate 的输出只会有一个。等所有的冒泡回调被执行完毕 click 函数才会被弹出栈。

最后注意,浏览器会尽量预先执行较为敏感的操作。

以上就是浅谈javascript事件环微任务和宏任务队列原理的详细内容,更多关于JavaScript 事件环的资料请关注我们其它相关文章!

(0)

相关推荐

  • JavaScript 判断数据类型的4种方法

    本文提供四种方法判断js数据类型,这里记录了它们之间的差异,分别是 typeof 运算符.instanceof 运算符.constructor 属性.Object.prototype.toString 方法. 一.使用 typeof 判断数据类型 console.log('测试 Number ->', typeof 1); // number console.log('测试 Boolean ->', typeof true); // boolean console.log('测试 String

  • 谈谈JavaScript中的函数

    JS中的函数简介 JS中的函数是一种通过调用来完成具体业务的一段代码块.最核心的目的是将可重复执行的操作进行封装,然后供调用方无限制的调用. JS中的函数的定义 JS中函数定义,有如下两种形式: 方式1 function f1(){} //函数声明,f1为函数名,可以将其理解为变量f1指向一个函数 function f2(){return 100;}//函数允许有返回值 function f3(a,b){}//函数中可以定义多个参数,无需指定变量类型 方式2 var f4=function(){

  • 关于JavaScript数组去重的一些理解汇总

    前言 做前端开发几年,在项目中用到数组去重的机会倒不是很多,但是在面试的时候却经常被问到,个人理解,这道题真正考的是对JavaScript的基础的掌握,因为有很多种方式可以做到.这次就根据这道题,将相关的知识理解透彻. 一.ES6中的new Set方式 先看看MDN上对Set的描述: Set 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用. Set对象是值的集合,你可以按照插入的顺序迭代它的元素. Set中的元素只会出现一次,即 Set 中的元素是唯一的. 关键字:任何类型都可以存储

  • JS事件循环机制event loop宏任务微任务原理解析

    首先看一段代码 async function (){ await f2() console.log('f1') } async function f2(){ console.log('f2') } console.log('正常1') f1() setTimeout(()=>{ console.log('定时器') }) console.log('正常2') 正确的打印顺序应该是:正常1,f2 ,正常2,f1,定时器 为什么会出现这样打印顺序呢 首先javascript是一门单线程语言,在最新的

  • JavaScript事件循环及宏任务微任务原理解析

    首先看一段代码: 打印顺序是什么? 正确答案:script start, script end, promise1, promise2, setTimeout 其中涉及到事件循环(event loop),宏任务(macrotask),微任务(microtask) 一.事件循环 Event Loop 程序中设置两个线程:一个负责程序本身的运行,称为"主线程":另一个负责主线程与其他进程(主要是各种I/O操作)的通信,被称为"Event Loop线程"(可以译为&quo

  • 浅析JavaScript 函数柯里化

    柯里化 (Currying)是把接收多个参数的原函数变换成接受一个单一参数(原来函数的第一个参数的函数)并返回一个新的函数,新的函数能够接受余下的参数,并返回和原函数相同的结果. ES6的方式实现柯里化的通用 function currying(fn,...rest1){ return function(...rest2){ //这里用apply 是为把数组形式的参数直接传入原函数 null是因为不需要改变this return fn.apply(null,rest1.concat(rest2)

  • JavaScript 事件代理需要注意的地方

    我们知道,如果给 form 里面的 button 元素绑定事件,需要考虑它是否会触发 form 的 submit 行为.除此之外,其它场合给 button 元素绑定事件,你几乎不用担心这个事件会有什么非预期的附加效果,很自然地会这样写事件处理代码: var button = document.querySelector('button') button.addEventListener('click', function (e) { console.log('点击了按钮') }) 你之所以放心这

  • JavaScript常用工具函数库汇总

    对象或数组的深拷贝 /** * 对象或数组的深拷贝 * @param {*} cloneObj 被克隆的对象 * @param {*} targetObj 克隆的目标对象 * @param {*} isOverride 若属性重复,是否覆盖被克隆对象的属性 */ function deepClone(cloneObj, targetObj, isOverride = true) { const _toString = Object.prototype.toString if (_toString

  • 浅谈javascript事件环微任务和宏任务队列原理

    JS 事件环 JS 程序的运行是离不开事件环机制的,这个机制保证在发生某些事情的时候我们有机会执行一个我们事先预定好的函数,事情发生的时候 JS 会将相应的函数入栈执行然后出栈,但是关于事件环我们还有一些未知的东西,例如,setTimeout 我们习惯称他为定时器,但是可能很多人没有意识到,这个东西和我们常用的一些事件没什么不同,只不过我们通常所说的事件大多需要用户触发,而 setTimeout 不用用户自己触发,而是指定时间之后触发:那么问题来了,如果我们将时间设置为 0 会发生什么?会立即执

  • 浅谈JavaScript事件绑定的常用方法及其优缺点分析

    传统方式  element.onclick = function(e){ // ... };  1.  传统绑定的优点 非常简单和稳定,可以确保它在你使用的不同浏览器中运作一致 处理事件时,this关键字引用的是当前元素,这很有帮组 2. 传统绑定的缺点 传统方法只会在事件冒泡中运行,而非捕获和冒泡 一个元素一次只能绑定一个事件处理函数.新绑定的事件处理函数会覆盖旧的事件处理函数 事件对象参数(e)仅非IE浏览器可用 W3C方式  element.addEventListener('click'

  • 浅谈JavaScript事件的属性列表

    HTML 4.0 的新特性之一是能够使 HTML 事件触发浏览器中的行为,比如当用户点击某个 HTML 元素时启动一段 JavaScript.下面是一个属性列表,可将之插入 HTML 标签以定义事件的行为. 属性 此事件发生在何时... onabort 图像的加载被中断. onblur 元素失去焦点. onchange 域的内容被改变. onclick 当用户点击某个对象时调用的事件句柄. ondblclick 当用户双击某个对象时调用的事件句柄. onerror 在加载文档或图像时发生错误.

  • 浅谈Javascript事件处理程序的几种方式

    事件就是用户或浏览器自身执行的某种动作.比如说click,mouseover,都是事件的名字.而相应某个事件的函数就叫事件处理程序(或事件侦听器).为事件指定处理程序的方式有好几种. 一:HTML事件处理程序. 如: 复制代码 代码如下: <script type="text/javascript"> function show(){ alert('hello world!'); } </script> <input type="button&q

  • 浅谈javascript事件取消和阻止冒泡

    取消默认操作 w3c的方法是e.preventDefault(),IE则是使用e.returnValue = false; 在支持addEventListener()的浏览器中,也能通过调用时间对象的preventDefault()方法取消时间的默认操作.不过,在IE9之前的IE中,可以通过设置事件对象的returnValue属性为false来达到同样的效果.下面的代码假设一个事件处理程序,它使用全部的三种取消技术: function cancelHandler(event){ var even

  • 浅谈Javascript事件对象

    如果是事件处理函数绑定的函数,浏览器会默认传递一个参数,而这个参数就是事件对象. document.onclick = function() { alert(arguments.length); //1 } 因为arguments[0]这样使用这个参数比较麻烦,所以我们可以传递一个参数evt来进行使用. document.onmouseup = function(evt) { var e = evt || window.event; alert(e.button); //0为鼠标左键,1为滚轮,

  • 浅谈Javascript事件模拟

    这就意味着会有适当的事件冒泡,并且浏览器会执行分配的事件处理程序.这种能力在测试web应用程序的时候,是非常有用的,在DOM 3级规范中提供了方法来模拟特定的事件,IE9 chrome FF Opera 和 Safari都支持这样的方式,在IE8及以前的办法的IE浏览器有他自己的方式来模拟事件 a)Dom 事件模拟 可以通过document上的createEvent()方法,在任何时候创建事件对象,此方法只接受一个参数,既要创建事件对象的事件字符串,在DOM2 级规范上所有的字符串都是复数形式,

  • 浅谈JavaScript宏任务和微任务执行顺序

    目录 一.JavaScript单线程 1. 同步任务(synchronous) 2. 异步任务(asynchronous) 二.任务队列(task queue) 1.执行栈 扩展一下setTimeout的理解 一.JavaScript单线程 JavaScript是单线程指的是同一时间只能干一件事情,只有前面的事情执行完,才能执行后面的事情.导致遇到耗时的任务时后面的代码无法执行. 在此之前啊 我们必须了解同步和异步 1. 同步任务(synchronous) console.log(123); c

  • 浅谈JavaScript 浏览器对象

    window window对象不但充当全局作用域,而且表示浏览器窗口. window对象有innerWidth和innerHeight属性,可以获取浏览器窗口的内部宽度和高度.内部宽高是指除去菜单栏.工具栏.边框等占位元素后,用于显示网页的净宽高.还有一个outerWidth和outerHeight属性,可以获取浏览器窗口的整个宽高. 补充: 网页可见区域宽:document.body.clientWidth 网页可见区域高:document.body.clientHeight 网页可见区域宽:

  • 浅谈javascript运算符——条件,逗号,赋值,()和void运算符

    前面的话 javascript中运算符总共有46个,除了前面已经介绍过的算术运算符.关系运算符.位运算符.逻辑运算符之外,还有很多运算符.本文将介绍条件运算符.逗号运算符.赋值运算符.()和void运算符 条件运算符 条件运算符是javascript中唯一的一个三元运算符(三个操作数),有时直接称做'三元运算符'.通常这个运算符写成'?:',当然在代码中往往不会这么简写,因为这个运算符拥有三个操作数,第一个操作数在'?'之前,第二个操作数在'?'和':'之间,第三个操作数在':'之后 varia

随机推荐