• 宏任务和微任务、事件循环、面试题


    一.JavaScript是单线程的,也就是说,同一个时刻,JavaScript只能执行一个任务,其他任务只能等待。

    二.为什么JavaScript是单线程的

    js是运行于浏览器的脚本语言,因其经常涉及操作dom,所以设置为单线程操作。如果是多线程的,也就意味着,同一个时刻,能够执行多个任务。试想,如果一个线程修改dom,另一个线程删除dom,那么浏览器就不知道该先执行哪个操作。所以js执行的时候会按照一个任务一个任务来执行。

    三. 为什么任务要分为同步任务和异步任务

    试想一下,如果js的任务都是同步的,那么遇到定时器、网络请求等这类型需要延时执行的任务会发生什么?

    页面可能会瘫痪,需要暂停下来等待这些需要很长时间才能执行完毕的代码

    所以,又引入了异步任务。

    • 同步任务:同步任务不需要进行等待可立即看到执行结果,比如console
    • 异步任务:异步任务需要等待一定的时候才能看到结果,比如setTimeout、网络请求

    四.事件循环

    浏览器本身是⼀个复杂的系统,它要做的事情⾮常多,例如: 执⾏js代码,请求图⽚资源,解 析css,渲染⻚⾯,响应⿏标的点击等等。在实现层⾯,浏览器内部会⽤不同的功能模块去完成不同的 事情。这些不同的模块就体现为进程。

    进⼀步把进程进⾏划分:

    1.主进程。⽤来协调控制其他⼦进程。2.GPU进程。⽤于3D绘制等。3.渲染进程。就是我们说的浏览器内核,负责具体⻚⾯的渲染,脚本执⾏,事件处理等。每个tab⻚背 后就有⼀个渲染进程。进程这个单位还是⽐较⼤,它进⼀步拆分多个线程。可以理解为⼀个⻚⾯上的事还是⽐较多,要多找些 ⼩弟来完成。具体来说,⼀个渲染进程包括:

    1.主线程。统⼀调度2.GUI渲染线程。负责渲染⻚⾯,布局和绘制。与JS引擎互斥。3.JS引擎线程。负责处理解析和执⾏javascript脚本程序。4.事件触发线程。⽤来控制事件循环(⿏标点击、setTimeout、ajax等)。当事件满⾜触发条件时, 将事件放⼊到JS引擎所在的执⾏队列中。5.setInterval与setTimeout所在的线程。定时任务并不是由JS引擎计时的,是由定时触发线程来计时 的。计时完毕后,通知事件触发线程6.异步http请求线程。浏览器有⼀个单独的线程⽤于处理AJAX请求,当请求完成时,若有回调函数, 通知事件触发线程。7.io线程。⽤来接收其他进程的消息。每个渲染进程都有⼀个主线程,并且主线程⾮常繁忙,既要处理 DOM,⼜要计算样式,还要处理布 局,同时还需要处理 JavaScript 任务以及各种输⼊事件。要让这么多不同类型的任务在主线程中有条不 紊地执⾏,这就需要⼀个系统来统筹调度这些任务,这个统筹调度系统就是消息队列和事件循环。

    任务有很多,⼈只有⼀个,且任意时刻只能做⼀件事(不是⼀边⾛路⼀边听课这种事哈),那怎么办, 就是排队呗

    这张图非常生动形象,大家可以收藏

    1.主线程上要做很多事情,例如:js代码执⾏,⻚⾯布局计算,渲染等2.主线程同⼀时刻只能做⼀件事,事情多了就要排队。所以主线程维护了任务队列。3.某个事件发⽣时,事件触发线程 就把对应的任务添加到主线程的任务队列中。4.主线程上的任务完成之后,就会从任务队列中取出任务来执⾏。任务是以事件及其回调的⽅式存在的。当事件(⽤户的点击,图⽚的成功加载)发⽣时,将其回调添加 到任务队列;主线程上的任务完成之后,就会从任务队列中取出任务来执⾏,此过程不断重复从⽽形成 ⼀个循环,称为eventLoop。

    要点回顾:

    ● 事件循环不是js的语⾔层⾯的内容,是js的宿主环境(浏览器,nodeJS)的讨论内容。在js代码中 讨论事件循环是没有意义的。

    ● 在更⼴的领域。事件循环是⼀个典型的⽣产者/消费者模型。异步I/O,⽹络请求是事件的⽣产者,源 源不断提供事件,这些事件被传递到对应的观察者那⾥,事件循环则从观察者那⾥取出事件并处 理。

    ● 在windows下,这个循环基于IOCP创建,⽽在linux下基于多线程创建。

    五.微任务和宏任务

    1.宏任务和微任务

    异步任务,⼜可以细分为宏任务和微任务。下⾯列举⽬前学过的宏任务和微任务。

    console.log('1')
    
    new Promise((resolve, reject) => {
    
     resolve('2')
    
    }).then((res) => {
    
     console.log(res)
    
    })
    
    setTimeout(() => {
    
     console.log('3')
    
    })
    
    new Promise((resolve, reject) => {
    
     resolve('4')
    
    }).then((res) => {
    
     console.log(res)
    
    })
    
    console.log('5') 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    效果:

    • 先执⾏同步代码* 遇到宏任务,放⼊队列* 遇到微任务,放⼊微任务队列* 执⾏栈为空将微任务放⼊栈执⾏* 所有的微任务完成之后,取出宏任务队列来执⾏

    六.⾯试题分析

    1.题目一

    console.log(1)
    
    setTimeout(function() {
    
     console.log(2)
    
    }, 0)
    
    const p = new Promise((resolve, reject) => {
    
     resolve(1000)
    
    })
    
    p.then(data => {
    
     console.log(data)
    
    })
    
    console.log(3) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    效果:

    2. 题目二

    console.log(1)
    
    setTimeout(function() {
    
     console.log(2)
    
     new Promise(function(resolve) {
    
       console.log(3)
    
       resolve()
    
    }).then(function() {
    
       console.log(4)
    
    })
    
    })
    
    new Promise(function(resolve) {
    
     console.log(5)
    
     resolve()
    
    }).then(function() {
    
     console.log(6)
    
    })
    
    setTimeout(function() {
    
     console.log(7)
    
     new Promise(function(resolve) {
    
       console.log(8)
    
       resolve()
    
    }).then(function() {
    
       console.log(9)
    
    })
    
    })
    
    console.log(10) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    效果:

    3.题目三

    console.log(1)
    
     setTimeout(function() {
    
       console.log(2)
    
    }, 0)
    
     const p = new Promise((resolve, reject) => {
    
       console.log(3)
    
       resolve(1000) // 标记为成功
    
       console.log(4)
    
    })
    
     p.then(data => {
    
       console.log(data)
    
    })
    
     console.log(5) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    效果:

    4.题目四

    new Promise((resolve, reject) => {
    
       resolve(1)
    
       new Promise((resolve, reject) => {
    
         resolve(2)
    
      }).then(data => {
    
         console.log(data)
    
      })
    
    }).then(data => {
    
       console.log(data)
    
    })
    
     console.log(3) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    效果:

    5.题目五

    setTimeout(() => {
    
     console.log(1)
    
    }, 0)
    
    new Promise((resolve, reject) => {
    
     console.log(2)
    
     resolve('p1')
    
     new Promise((resolve, reject) => {
    
       console.log(3)
    
       setTimeout(() => {
    
         resolve('setTimeout2')
    
         console.log(4)
    
      }, 0)
    
       resolve('p2')
    
    }).then(data => {
    
       console.log(data)
    
    })
    
     setTimeout(() => {
    
       resolve('setTimeout1')
    
       console.log(5)
    
    }, 0)
    
    }).then(data => {
    
     console.log(data)
    
    })
    
    console.log(6) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    效果:

    七.总结

    事件循环比较简单,它是一个在 “JavaScript 引擎等待任务”,"执行任务"和"进入休眠状态等待更多任务"这几个状态之间转换的无限循环。

  • 相关阅读:
    出现很多DEBUG org.apache.http.**日志,如何关闭
    数据结构之AVL树
    学习vue生命周期心得
    GOM跟GEE登陆器列表文件加密教程
    TimeWise-Jira工时管理插件6.0.0发布!对比测评某知名工时插件,谁的数据处理性能更胜一筹?
    工业CT检测技术及工业CT基本组成
    数字孪生技术助力水务行业实现智能化
    历时数月钻研推流/对比各种流媒体服务程序/PK总结
    [MATLAB学习]:Matlab生成滑动平均滤波算法文件并移植到STM32单片机上运行——基于CubeMX
    万字手撕——Java集合源码
  • 原文地址:https://blog.csdn.net/weixin_53312997/article/details/126604508