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
原文地址:https://www.cnblogs.com/baifangzi/p/15636398.html