JavaScript事件循环机制

作者: 一蓑烟雨任平生_cui | 来源:发表于2019-09-27 18:01 被阅读0次
  1. js是单线程的,该线程中有唯一一个事件循环,任务队列可以有多个

  2. JavaScript代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。


    队列数据结构
  3. 任务队列: 宏任务和微任务

  • 宏任务:script(整体代码)、setTimeout、setInterval、setImmediate、(I/O、UI rendeing这俩没研究)
  • 微任务:Promise、Node中的process.nextTick、html5新特性MutationObserver(没研究)
  1. setTimeout/Promise等我们称之为任务源(任务分发器)。而进入任务队列的是他们指定的具体执行任务。
  2. 来自不同任务源的任务会进入到不同的任务队列。其中setTimeout与setInterval是同源的。
  3. 其中每一个任务的执行,无论是macro-task还是micro-task,都是借助函数调用栈来完成。

关于setImmediate:MDN解释

  1. 该特性是非标准的,请尽量不要在生产环境中使用它!
  2. 该方法用来把一些需要长时间运行的操作放在一个回调函数里,在浏览器完成后面的其他语句后,就立刻执行这个回调函数。
  3. 注意: 该方法可能不会被批准成为标准,目前只有最新版本的 Internet Explorer 和Node.js 0.10+实现了该方法。它遇到了 Gecko(Firefox) 和Webkit(Google/Apple) 的阻力。

浏览器执行

console.log('global1')

setTimeout(() => {
    console.log('setTimeout1')
    new Promise(resolve => {
            console.log('p1')
            resolve()
        })
        .then(() => {
            console.log('p1-then')
        })

    new Promise(resolve => {
            console.log('p2')
            resolve()
        })
        .then(() => {
            console.log('p2-then')
        })
})

setTimeout(() => {
    console.log('setTimeout2')

    new Promise(resolve => {
            console.log('p3')
            resolve()
        })
        .then(() => {
            console.log('p3-then')
        })
})

new Promise(resolve => {
        console.log('p5')
        resolve()
    })
    .then(() => {
        console.log('p5-then')
    })

console.log('global2')

输出顺序:

// global1 p5 global2 p5-then setTimeout1 p1 p2 p1-then p2-then setTimeout2 p3 p3-then

浏览器执行机制:

  1. script (这个过程中执行同步代码,宏任务加入相应类型的队列,微任务加入相应类型的队列)
  2. 执行清空所有微任务
  3. 执行第一个加入队列的宏任务 setTimeout (setImmediate的任务队列会在setTimeout队列的后面执行),并将该过程中产生的微任务加入相应的队列
  4. 执行清空所有微任务
  5. 执行第二个宏任务

...

以此类推,以一宏任务---所有微任务的顺序交替执行

Node执行

console.log('global1')

setImmediate(() => {
    console.log('setImmediate1')
    new Promise(resolve => {
            console.log('p1')
            resolve()
        })
        .then(() => {
            console.log('p1-then')
        })
})

setTimeout(() => {
    console.log('setTimeout1')
    new Promise(resolve => {
            console.log('p2')
            resolve()
        })
        .then(() => {
            console.log('p2-then')
        })

    new Promise(resolve => {
            console.log('p3')
            resolve()
        })
        .then(() => {
            console.log('p3-then')
        })
})

setTimeout(() => {
    console.log('setTimeout2')

    new Promise(resolve => {
            console.log('p4')
            resolve()
        })
        .then(() => {
            console.log('p4-then')
        })

    process.nextTick(() => {
        console.log('process1')
    })
})

new Promise(resolve => {
        console.log('p5')
        resolve()
    })
    .then(() => {
        console.log('p5-then')
    })

process.nextTick(() => {
    console.log('process2')
})

console.log('global2')

Node输出:

// global1 p5 global2 process2 p5-then setTimeout1 p2 p3 setTimeout2 p4 process1 p2-then p3-then p4-then setImmediate1 p1  p1-then

Node执行机制:

  1. 同浏览器
  2. 执行清空所有微任务 (执行微任务时,nextTick队列会比Promie先执行。nextTick中的可执行任务全部执行完毕之后,才会开始执行Promise队列中的任务。)
  3. 执行某一类(某一队列)宏任务(先执行完所有setTimeout宏任务,将该过程产生的微任务加入相应队列)
  4. 执行清空所有微任务
  5. 执行另一队列宏任务 setImmediate

...
以此类推,以一(队列)宏任务---所有微任务的顺序交替执行。

任务队列和函数调用栈

相关文章

网友评论

    本文标题:JavaScript事件循环机制

    本文链接:https://www.haomeiwen.com/subject/vxyauctx.html