• js事件轮询(event loop)


    前言

    事件轮询,事件循环,event loop,叫法各有不同但描述的都是js的事件执行的顺序,描述了js以何种规则执行。

    事件轮询作为js的核心机制是必须要掌握的,编写此篇文章也是做下相关知识的整理,分享给大家。

    面试造飞机,上班拧螺丝

    说到事件轮询这个面试不得不问的基础知识,就想谈下 “ 面试造飞机,上班拧螺丝 ”这个经典理论。

    设计模式、源码、事件轮询、闭包、函数式编程、柯里化、高阶组件等等,对于面试常谈的这些理论,其实很多都是会在日常中体现,理解这些设计理念也可以提高技术水平,设计模式中代理模式、策略模式等都是比较常用的,函数式编程在如今vue3和react的时代也是屡见不鲜。

    算法略有区别,递归比较常用,但动态规划等使用较少,但算法重要的是提高思维水平和拓宽思维方式,不仅适用于解题,也适用于平常问题的处理和心态的提升。

    事件轮询

    js是单线程阻塞型,当然也可以使用web  workers来开启多线程,当前只介绍单线程的事件轮询机制。单线程阻塞下才有事件轮询,这个是js这个语言的特性。

    事件轮询描述的是js的执行顺序,js中因为单线程的特性想要类似多线程执行衍生出了同步任务和异步任务(分为宏任务和微任务),异步任务不阻塞,例如promise待返回结果之后再执行.then函数。

    宏任务:

    script(整体代码)、setTimeout、setInterval、setImmediate(Node.js环境)、I/O操作(如Node.js中的read/write)、UI渲染(浏览器独有)等。

    微任务:

    Promise.then、process.nextTick(Node.js环境)、MutationObserver(浏览器环境)。

    介绍

    以代码执行顺序而言,先代码同步执行,先同步后异步,异步中执行一个宏任务之后清空微任务,然后再执行一个宏任务,然后再清空微任务。

    !!new promise是同步任务,promise.then才是微任务。

    练习部分,可以先自己做一下,然后不懂之处借鉴之后的讲解部分。

    练习一

    1. setTimeout(() => {
    2. console.log("setTimeOut start");
    3. }, 0);
    4. function promise2() {
    5. return new Promise((resolve) => {
    6. console.log("promise2");
    7. resolve();
    8. });
    9. }
    10. async function async1() {
    11. console.log("async1 start");
    12. await promise2();
    13. console.log("async1 end");
    14. }
    15. async1();
    16. async1();
    17. console.log("script end");
    '
    运行

    讲解一

    !!new promise中的是同步任务,promise.then才是异步任务的微任务

    代码开始执行,遇到setTimeout放入宏任务队列

    然后执行到async1(),这里执行两次是为了清晰的展示宏任务之后会清空微任务,开始打印async1 start ,然后await直接执行打印promise2,然后将打印async1 end这个微任务放入微任务队列,

    然后async1()和上一个async1()一样,然后执行同步任务打印script end

    然后同步任务这个大的宏任务执行完之后执行所有的微任务,打印两次async1 end

    然后再拿宏任务队列的首元素执行打印setTimeOut start

    然后再执行微任务队列的所有任务,微任务队列为空,再抽宏任务队列的首元素执行

    宏任务队列为空代码执行结束。

    async1 start  -> promise2  -> async1 start  -> promise2  -> script end ->  async1 end -> async1 end -> setTimeOut start

    练习二

    1. console.log(1)
    2. setTimeout(function () {
    3. console.log(6)
    4. }, 1000)
    5. new Promise(function (resolve, reject) {
    6. console.log(2)
    7. setTimeout(function () {
    8. console.log(3)
    9. resolve()
    10. }, 500)
    11. }).then(function (res) {
    12. console.log(4)
    13. setTimeout(function () {
    14. console.log(7)
    15. }, 500)
    16. })
    17. console.log(5)
    '
    运行

    讲解二

    script  宏任务

    同步任务,打印1

    异步任务-宏任务,放入宏任务队列

    new promise同步任务直接执行,打印2,setTimeout 宏任务继续放入宏任务队列

    同步任务,打印5

    微任务

    微任务为空抽一个宏任务执行

    宏任务

    setTimeout(function () {

        console.log(6)

    }, 1000)

    宏任务队列首元素执行,延时函数生效,1秒(1000ms为1s)后打印

    微任务

    微任务队列为空,抽宏任务队列首元素执行

    宏任务

        setTimeout(function () {

            console.log(3)

            resolve()

        }, 500)

    打印3,resolve之后把promise.then放到微任务队列

    微任务

    执行promise.then,直接打印4,然后把setTimeout放入宏任务队列

    宏任务

    执行宏任务队列中的首元素,延时函数生效,0.5秒后打印7

        setTimeout(function () {

            console.log(7)

        }, 500)

    开始的1000毫米的延时函数虽然生效早,但是延时长,所以先打印3,两个500ms和先生效的1000ms相等,但是1000ms的先生效,所以顺序是 3 -> 6 -> 7

    最后其实还有一次清空微任务和抽取宏任务的操作,和练习一相同省略。

    1 -> 2 -> 5 -> 3 -> 4 -> 6 -> 7

    练习三

    将练习二中的最后执行的延时函数的500ms改为小于500ms的数值会怎样(两个延时函数相加小于1000)

    讲解三

    答案是 1 -> 2 -> 5 -> 3 -> 4 -> 7 -> 6

    7和6顺序颠倒,这里是有一个小坑就是延时函数执行之后不会立即打印,一段时间之后才会生效,所以会有差异。

    结语

    讲解二略冗长是为了更为清晰的介绍事件轮询机制,核心是这个机制,练习是帮助理解。不对之处,欢迎指正,模糊之处,欢迎讨论。

  • 相关阅读:
    csgo 闪退
    mysql基础部分第一次复习(1-8章,到聚合函数)
    EM聚类(下):用EM算法对王者荣耀英雄进行划分
    什么是接触电流怎么测?
    QEMU开发入门
    mysql基础语句
    Okhttp通用工具类
    被误删的HDFS文件如何有效恢复
    RHCSA --- Linux存储管理
    echarts+node+ajax实现时间天气服务器
  • 原文地址:https://blog.csdn.net/zkx529/article/details/139398393