zoukankan      html  css  js  c++  java
  • js事件循环机制

    单线程的Js

    首先要明确js单线程运行,所以代码按照出现顺序运行,所以什么是线程?什么是进程?

    进程与线程

    一个进程包含一个或多个线程

    进程

    • 进程是资源分配的最小单位,通俗点说,打开一个软件,他就会创建一个或多个进程,打开电脑任务管理器去查看一下进程

    线程

    • 线程是运行调度的最小单位,通俗点说就是这个软件程序是怎么运行的,是单线程的,按顺从头到尾运行;还是多线程的,两个及以上的线程在同时运行

    单线程的原因

    如果是多线程的话,那么代表我可以同时执行两个操作,于是在一个线程上添加了一个标签,在另一个线程上又删除了这个标签,这样就产生混乱,所以他必须是单线程执行程序

    同步与异步

    既然是单线程执行程序,那么看一段代码

    console.log(1)
    setTimeout(() => {
        console.log(2)
    }, 0)
    console.log(3)
    // 输出132
    

    如果是按单线程自上而下预定的话,预期是输出123,但是实际输出了132,这种行为又与JS的同步异步操作有关,他与单线程并不冲突

    同步

    同步代表比虚等待当前操作执行完毕才能执行下一个操作,比如

    console.log(1)
    console.log(2)
    alert('hello')
    console.log(3)
    //输出123
    

    那么就又存在一个问题:当前操作耗费的时间过长,不能及时运行下面的代码,造成阻塞,比如alert一个窗口之后,必须点击确定才能运行下面的代码,又或者要读取一个大文件,读取完成之前操作不了任何东西。为了解决这个问题,提出了异步

    异步

    不会立即执行,而是放入到一个队列中,等待同步操作执行完之后再执行异步。什么操作会是异步操作?常见的有:

    • setTimeout,setInterval,setImmediate
    • 请求 promise,axios ,ajax

    总结

    重新看一下这段代码就很容易理解:

    • console.log(1)是同步操作输出1
    • 遇到setTimeout是异步操作,放入待执行的队列
    • console.log(3)是同步操作输出3
    • 同步操作都执行完毕了,执行待执行队列中的操作,输出2

    上面这个步骤就是一个简单的事件循环机制,继续深入事件循环

    console.log(1)
    setTimeout(() => {
        console.log(2)
    }, 0)
    console.log(3)
    

    事件循环

    能触发异步的操作有很多,那么多个存在多个异步操作,这些操作的顺序又是怎样的

    console.log(1)
    setTimeout(() => console.log(2))
    new Promise((resolve) => {
        console.log(3)
        resolve()
    }).then(() => {
        console.log(4)
    }) // 输出1342
    

    如果是异步操作的优先级相同,setTimeout应该Promise.then前面执行,但是实际情况不是。由此可见,异步中各种操作的优先级并不是相同的。

    Js对任务又可以分为宏任务微任务

    宏任务(macro-task)

    宿主环境提供的方法

    • 整体script代码
    • setTimeout
    • setInterval
    • setImmediate
    • I/O
    • UI render

    微任务(micro-task)

    js引擎提供的

    • process.nextTick
    • Promise.then
    • Async/Await(实际就是promise)
    • MutationObserver(html5新特性)

    事件循环过程

    image-20210723102608347

    • 代码自上而下运行,判断是同步还是异步,同步操作直接运行。异步操作放入任务队列中,在EventTable中注册函数,然后推入到EventQueue
    • 所有的同步操作执行完毕之后,会去任务队列中获取待执行的任务,判断是否存在任务
    • 如果队列中存在任务。先执行宏任务,然后执行该宏任务产生的微任务
    • 若在执行微任务的过程中,产生了新的微任务,则继续执行微任务,
    • 等到该宏任务里面的所有微任务执行完毕之后,会去重新检查任务队列,执行再执行下一个宏任务。
    • 所有的宏任务完成之后,则完成了此次的所有任务操作

    解析一下这段代码

    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)
            new Promise(function (resolve) {
                console.log(9)
                resolve()
            }).then(function () {
                console.log(10)
            })
            resolve()
        }).then(function () {
             new Promise(function (resolve) {
                console.log(11)
                resolve()
            }).then(function () {
                console.log(12)
            })
            console.log(13)
        })
    })
    console.log(14)
    
    // 1 5 14 6 2 3 4 7 8 9 10 11 13 12
    
    • 顺序执行,遇到了三个同步操作 1,5,14,按照出现的顺序输出,两个setTimeout创建了两个宏任务,一个new Promise().then创建了一个微任务
    • 整体代码是一个script宏任务,然后开始执行这个宏任务中的微任务,输出6
    • 当前宏任务里面的微任务也执行完毕,开始执行下一个宏任务,进入到第一个setTimeout中,遇到两个同步操作,输出 2,3
    • 在这个宏任务里面通过then又创建了一个微任务,执行输出4
    • 进入到第二个setTimeout中执行同步操作,输出7,8,9
    • 碰到then又创建了一个微任务,输出10,
    • 上个微任务执行完毕之后进入到外层的then里面,这时又创建了一个微任务继续执行输出11,13
    • 进入到最后一个then里面输出12
  • 相关阅读:
    MySQL基准测试--innodb_buffer_pool_instances
    MySQL参数优化:back_log
    MySQL open_files_limit相关设置
    Django权限系统auth模块详解
    2.9 go mod 之本地仓库搭建
    my40_MySQL锁概述之意向锁
    my39_InnoDB锁机制之Gap Lock、Next-Key Lock、Record Lock解析
    2.8 GO 参数传递
    my38_MySQL事务知识点零记
    my37_MGR流控对数据库性能的影响以及MGR与主从的性能对比
  • 原文地址:https://www.cnblogs.com/baifangzi/p/15636398.html
Copyright © 2011-2022 走看看