• JS事件循环机制


    JS事件循环机制

    先记录两个任务队列:宏任务(macro-task)微任务(micro-task)
    宏任务:script(整体代码),setTimeout,setInterval,setImmediate,I/O,UI rendering
    微任务:process.nextTick, Promise.then等,Object.observe(用于观察对象的变动),MutationObserver(用于监视DOM变动)。
    其中:setImmediate和process.nextTick是nodejs里的API,浏览器中并没有

    在调用栈中遇到DOM操作、ajax操作以及setTimeout等WEBAPIs的时候会交给浏览器内核其他模块先行处理,如DOM Binding、network和timer分别处理上述三种API。等这些模块处理完这些操作的时候将回调函数放入任务队列中,之后等待栈中任务执行完后去任务队列中调用回调函数。

    例:通过setTimeout说明事件循环机制

    console.log(1);
    setTimeout(function cb(){
    	console.log('2');
    }, 3000);
    console.log(3);
    
    • 1
    • 2
    • 3
    • 4
    • 5

    执行步骤:
    1、首先全局执行上下文入栈,全局上下文会位于调用栈最底部,形成一个全局对象
    在这里插入图片描述
    2、代码接着执行,遇到console.log(1);由于是同步任务,立即执行,此时输出1,执行完毕后console.log(1)出栈
    在这里插入图片描述
    3、代码接着执行,遇见setTimeout,执行引擎先将其添加到调用栈,但调用栈发现其为WEBAPIs中的API,将其出栈,并将延时执行函数交由timer模块处理。此时代码继续执行console.log(3);输出3,执行完毕后出栈

    在这里插入图片描述
    4、timer模块处理延时完成,将cb()放入任务队列,此时调用栈任务已执行完毕
    在这里插入图片描述
    5、由于调用栈任务已完成,会前往任务队列中查看是否有需要执行的回调函数,此时会将cb()添加到调用栈中,并执行里面console.log(3)方法,输出3,等到任务执行完毕后再出栈。
    在这里插入图片描述
    事件循环的顺序是从script开始第一次循环,然后全局上下文进入调用栈,碰到宏任务就将其交给处理它的模块,处理完成后放入宏任务的任务队列中。微任务就放入微任务队列。上一个宏任务执行完成后会检查微任务队列中是否有任务,如果有微任务就执行所有微任务,执行完毕后再去执行下一个宏任务

    注意:1、任务队列分为宏任务队列和微任务队列
    2、宏任务队列可以有多个,第一个宏任务队列只有一个宏任务为执行主线程中的js代码。微任务队列只能有一个。

    总而言之

    1、新程序script执行归为宏任务
    2、同步任务执行完成之后会先去执行微任务,再去执行宏任务
    3、不同任务会放入不同的任务队列
    4、先执行宏任务,再执行微任务队列中的所有微任务,再执行下一条宏任务,如此循环
    5、当有多个宏任务和微任务的时候,事件循环顺序是按文章开头列出的宏任务和微任务的顺序执行
    流程:
    在这里插入图片描述

    记录一道坑题

    const button = document.querySelector('button');
    // 点击1
    button.addEventListener('click', () => {
        Promise.resolve().then(() => {
            console.log('microtask 1')
        });
        console.log('listener 1');
    })
    
    // 点击2
    button.addEventListener('click', () => {
        Promise.resolve().then(() => {
            console.log('microtask 2')
        });
        console.log('listener 2');
    })
    
    // 触发方式1: 手动点击按钮触发
    // 触发方式2: button.click();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    触发方式1触发结果:
    依次打印 listener 1 — microtask 1 — listener 2 — microtask 2
    为啥呢?查询资料加上自己的理解:
    点击时,js调用栈会添加点击1的宏任务,执行addEventListener同步任务,内部嵌套了console.log()的同步任务,优先执行,执行完毕后执行promise的then微任务,所以打印 listener 1 — microtask 1 。执行完毕后点击1的宏任务出栈,添加点击2的宏任务,再执行点击2内部的任务,打印 listener 2 — microtask 2
    触发方式2触发结果:
    依次打印listener 1 — listener 2 — microtask 1 — microtask 2
    执行顺序:
    先向调用栈添加click的宏任务,click中有两个同步任务点击1和点击2,在点击1中promise.then()是微任务,放入微任务队列,打印 listener 1,点击1打印完成后,click的宏任务并没有执行完成,会继续向下执行点击2的同步任务,点击2中promise.then()添加到微任务队列,打印listener 2,此时宏任务中挂载了两个微任务,检查微任务队列,依次打印microtask 1和microtask 2。

    也就是说:触发方式1代码依次执行会创建两个点击事件的宏任务,触发方式2是创建一个click事件宏任务。有问题之处恳请指正

  • 相关阅读:
    Fastjson反序列化漏洞
    [BJDCTF2020]Cookie is so stable-1|SSTI注入
    IDEA (任意 JetBrains IDE)拆分先前 commit
    如何在2.2.1版Aduino IDE中开发ESP32
    Android app保活(前台服务)
    vscode 乱码解决
    深入理解Vue3.js响应式系统设计之调度执行
    VLOOKUP 函数出现 #N/A 错误的 4 种情况(附解决方法)
    清华美院「后羿雕塑」像外国人,引全网争议.....
    SpringCloud五大组件原理及面试题
  • 原文地址:https://blog.csdn.net/weixin_42178670/article/details/127390640