JS事件循环,MACRO TASK,MICRO TASK

news/2024/7/7 13:20:15

事件循环的基本概念

  • JS执行的过程中,由JS引擎控制的函数调用栈来控制时间循环
  • 定时器线程,事件触发线程,异步http请求线程控制异步的任务队列
  • 任务分为macro task,micro task 对应都有不同的任务队列
    • macro task:script正常代码,setTimeout,setInterval,I/O,UI rendering
      •   由事件触发线程维护  
    • micro task:process.nextTick,promise,mutationObserve 
      •   由JS引擎线程维护
    • 最终在函数调用栈中完成

事件循环执行的顺序

  • 执行函数调用栈中的macro task,直到调用栈清空(剩下全局)
  • 执行job queues中所有可执行的micro tasks
  • 执行UI render
  • 从事件队列中获取macro task,开始新的事件循环

例子

<div class="outer" style="width:200px;height:200px;background-color: #ccc">
    1
    <div class="inner" style="width:100px;height:100px;background-color: #ddd">begin</div>
</div>
// Let's get hold of those elements
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');

var i = 0;

// 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() {
    i++;

    if(i === 1) {
        inner.innerHTML = 'end';
    }

    console.log('click');

    setTimeout(function() {
        alert('锚点');
        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 div 时程序依次的执行顺序是:

  1. onclick 入 JS stack
  2. 打印出 click
  3. 将 timeout 压入到 macrotask
  4. 将 promise 压入到 microtask
  5. 修改 outer 属性 data-random
  6. 将 mutate 压入到 microtask,
  7. onclick 出 JS stack

此时,由于用户点击事件onclick产生的macrotask执行完毕,JS stack 清空,开始执行microtask.

  1. promise 入 JS stack
  2. 打印出 promise
  3. promise 出 JS stack
  4. mutate 入 JS stack
  5. 打印出 mutate
  6. mutate 出 JS stack

此时,microtask 执行完毕,JS stack 清空,但是由于事件冒泡,接着执行outer上的onclick事件.

  1. onclick 入 JS stack
  2. 打印出 click
  3. 将 timeout 压入到 macrotask
  4. 将 promise 压入到 microtask
  5. 修改 outer 属性 data-random
  6. 将 mutate 压入到 microtask,
  7. onclick 出 JS stack

此时,由于outer上的onclick事件产生的macrotask执行完毕,JS stack 清空,开始执行microtask.

  1. promise 入 JS stack
  2. 打印出 promise
  3. promise 出 JS stack
  4. mutate 入 JS stack
  5. 打印出 mutate
  6. mutate 出 JS stack

此时,本轮事件循环结束,UI 开始 render.

  1. 页面中inner的innerHTML变为end

此时,UI render 完毕,开始下一轮事件循环.

  1. timeout 入 JS stack
  2. 弹出警告 锚点.
  3. 打印出 timeout
  4. timeout 出 JS stack
  5. timeout 入 JS stack
  6. 弹出警告 锚点.
  7. 打印出 timeout
  8. timeout 出 JS stack

到此为止,整个事件执行完毕,我们可以看到在弹出警告框之前inner的内容已经改变

那如果不是用户点击事件触发onclick,而是js触发呢?
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);
inner.click();

此时的执行顺序是:

  1. 首先是script(整体代码)入 JS stack
  2. onclick 入 JS stack
  3. 打印出 click
  4. 将 timeout 压入到 macrotask
  5. 将 promise 压入到 microtask
  6. 修改 outer 属性 data-random
  7. 将 mutate 压入到 microtask,
  8. onclick 出 JS stack

此时,inner 的 onclick 已经出 JS stack,但是script(整体代码)还没有出 JS stack,还不能执行microtask,由于冒泡,接着执行 outer 的 onclick.

  1. onclick 入 JS stack
  2. 打印出 click
  3. 将 timeout 压入到 macrotask
  4. 将 promise 压入到 microtask
  5. 修改 outer 属性 data-random

接着执行的outer.setAttribute('data-random', Math.random());,但是由于上一个mutation microtask还处于等待状态,不能再添加mutation microtask,所以这里不会将 mutate 压入到 microtask。接着执行:

  1. onclick 出 JS stack
  2. script(整体代码)出 JS stack

此时,inner.click()执行完毕,script(整体代码)已出 JS stack,JS stack 清空,开始执行mircotask.

  1. promise 入 JS stack
  2. 打印出 promise
  3. promise 出 JS stack
  4. mutate 入 JS stack
  5. 打印出 mutate
  6. mutate 出 JS stack
  7. promise 入 JS stack
  8. 打印出 promise
  9. promise 出 JS stack

此时,所有的mircotask执行完毕,本轮事件循环结束,UI 开始 render.

  1. 页面中inner的innerHTML变为end

此时,UI render 完毕,开始下一轮事件循环.

  1. timeout 入 JS stack
  2. 弹出警告 锚点.
  3. 打印出 timeout
  4. timeout 出 JS stack
  5. timeout 入 JS stack
  6. 弹出警告 锚点.
  7. 打印出 timeout
  8. timeout 出 JS stack

到此为止,整个事件执行完毕,我们可以看到在弹出警告框之前inner的内容已经改变

 

 

参考文献:

https://segmentfault.com/a/1190000013212944

http://zhangxiang958.github.io/2018/02/03/Event%20Loop%20中的%20microtask%20与%20macrotask/

 

转载于:https://www.cnblogs.com/ninalei/p/8637981.html


http://www.niftyadmin.cn/n/3021130.html

相关文章

苏州大学的计算机技术好考吗,苏州大学考研难吗?一般要什么水平才可以进入?...

首先&#xff0c;申明一点&#xff1a;考研本身就不是一件容易的事情&#xff0c;在考研的过程中&#xff0c;找准自己的定位、学会搜集资料、搜集信息并且辅之于踏实的备考、准确的方法是至关重要的。同时&#xff0c;要学会坚持&#xff0c;不忘初心&#xff0c;很多同学在开…

DTE设备和DCE设备详细介绍

DTE设备 和DCE设备 详细 介绍 DTE英文全称Data Terminal Equipment&#xff0c;数字终端设备&#xff0c;指一般的终端或是计算机。可能是大、中、小型计算机&#xff0c;也可能是一台只接收数据的打印机。 DCE英文全称Data Circuit-terminating Equipment&#xff0c;数…

计算机无法登录到网络,电脑无法连接到这个网络是什么原因

以联想Y7000&#xff0c;Win10系统为例&#xff0c;具体的操作方法如下&#xff1a;1、可能是因为无线网络的密码被修改&#xff0c;从而导致电脑无法连接到网络。因此&#xff0c;在电脑桌面右键单击鼠标&#xff0c;找到并打开控制面板选项&#xff0c;在界面中选择网络和int…

db2的编目(建立一个客户端到服务器端的连接)

首先查找出DB2占用的端口号 使用命令 db2 get dbm cfg|grep SVCENAME 我的服务器输出&#xff1a; TCP/IP Service name (SVCENAME) db2c_db2inst1 在/etc/services文件中找对应的端口号 我的服务器查到&#xff1a;db2c_db2inst1 50001/tcp 在…

【C++】CCFCSP201803-2碰撞的小球

// // main.cpp // CCFCSP20180318_2_碰撞的小球 // // Created by T.P on 2018/3/24. // Copyright © 2018年 T.P. All rights reserved. // /* 碰撞的小球 1.0s 256.0MB问题描述数轴上有一条长度为L&#xff08;L为偶数)的线段&#xff0c;左端点在原点&#xff0c;…

计算机组成与设计实验西工大,西工大-组成原理实验报告.pdf

课中检查完成的题号评语:及题数&#xff1a;课后完成的题号与题数&#xff1a;成 自评95绩: 成绩:实验报告基于 Verilog 语言的运算器和存实验 日 2015.11.储器名称&#xff1a; 期&#xff1a; 4设计与实现班 学 姓张林江级&#xff1a; 号&#xff1a; 6 名&#xff1a;一、实…

OSPF路由器邻接关系的详细建立过程

运行OSPF 协议的路由器&#xff0c; 如果你想正常运转的话&#xff0c;那么就得和其他的路由器建立完全邻接的关系。因为这种状态下&#xff0c;同一个区域中的所有的路由器的LSDB都是完全同步的、一致的。呵 呵、、、其实呢&#xff0c;应该反过来说&#xff0c;当同一个区域…

计算机博弈点格棋规则,点格棋计算机博弈平台通信接口.pdf

计 算 机 与 现 代 化2016年第 3期 JISUANJIYUXIANDAIHUA 总第247期文章编号 &#xff1a;1006-4275(2016)03-0096-04点格棋计算机博弈平台通信接 口张利群 &#xff0c;曹 杨 &#xff0c;李 厦(1&#xff0e;辽宁石油化工大学计算机与通信工程学院&#xff0c;辽宁 抚顺 1130…