javascript事件循环(Event Loop)

231 阅读4分钟

  1. javascript事件循环结构 主要组成:执行栈、堆内存、消息队列

    执行栈(stack):存储并且执行同步任务,等到同步任务执行完,执行栈会清空,也就是执行栈处于空闲状态,执行栈再执行消息队列里的异步任务。*执行栈遵循的是先进先出原则

    堆内存(heap):存储变量和对象

    消息队列(queue):消息队列分为宏任务队列和微任务队列,存放异步任务,异步任务主要包括:DOM事件监听、计时器(setTimeout)、网络请求(ajax)

  2. 事件循环的过程

    (1)任务进入执行栈,先判断是同步任务还是异步任务,如果是同步任务,主线程就会把同步任务放到执行栈中执行,遵从先进先出的原则;如果是异步任务,交给webAPIS处理,存储到消息队列中,等待执行

    (2)同步任务全部执行完以后,主线程读取消息队列,先去执行消息队列里的微任务,微任务全部执行完,释放清空,然后再去开始执行宏任务队列

    (3)循环,重复上述2步骤

  3. 宏任务和微任务

    (1)宏任务:setTimeout、setInterval、I/O

    (2)微任务:promise

    执行栈执行完同步任务后,释放清空执行栈,处于空闲状态,主线程就会去读取消息队列,消息队列里有任务,执行栈就会去执行消息队列

    具体步骤:执行栈执行完同步任务后,判断是否有可执行的微任务,有微任务则执行所有微任务,执行完微任务,开始新的宏任务,开始下一次循环;没有微任务则直接开始执行下一个宏任务,开始下一次循环;执行完宏任务,再去判断有没有微任务,重复上述步骤。异步任务中(即消息队列中),执行下一个宏任务之前,会执行完本次循环中的所有微任务

  4. 例子

    console.log('start')

    const interval = setInterval(() => {  
        console.log('setInterval')
    }, 0)

    setTimeout(() => {  
        console.log('setTimeout 1')
        Promise.resolve().then(() => {
            console.log('promise 3')
        }).then(() => {
            console.log('promise 4')
        }).then(() => {
            setTimeout(() => {
                console.log('setTimeout 2')
                Promise.resolve().then(() => {
                    console.log('promise 5')
                }).then(() => {
                    console.log('promise 6')
                }).then(() => {
                    clearInterval(interval)
                })
            }, 0)
        })
    }, 0)
    
    Promise.resolve().then(() => {  
        console.log('promise 1')
    }).then(() => {
        console.log('promise 2')
    })

第一轮:

整段代码被当做macrotask执行
console.log('start') 打印‘start’
interval = setInterval(() => {})回调函数()=>{}放入消息队列的macrotask中,此时消息队列的macrotask队列为[interval]
setTimeout(()=>{})回调函数()=>{}放入消息队列的macrotask,此时消息队列的macrotask队列为[interval, setTimeout1]
执行promise,并且调用resolve方法,触发了回调函数,这时候把回调函数添加到消息队列中的microtask中,此时microtask为['promise1', 'promise2']
执行栈执行完同步任务,释放清空,处于空闲状态
第一轮执行结果:'start'

第二轮:

先执行消息队列中的microtask queue中的回调函数,打印promise1,promise2;microtask queue为空
从macrotask queue [interval,setTimeout1]中取出interval,打印setInterval,执行完释放掉,此时macrotask queue [setTimeout1]
setInterval的回调函数添加到消息队列的macrotask queue中,此时macrotask为[setTimeout1, setInterval]
执行栈为空,microtask queue为空,开始下一轮循环
第二轮执行结果:'start' 'promise1' 'promise2' 'setInterval'

第三轮:

取出macrotask queue中的最早的任务,setTimeout1,打印setTimeout1,释放掉setTimeout1,此时macrotask queue为[setInterval]
执行promise,调用了resolve方法,触发回调,回调函数被存放在microtask queue中,此时microtask queue为[promise3, promise4, setTimeout2]
执行栈为空,放入执行栈执行microtask queue,打印出promise3, promise4。setTimeout2是异步函数,setTimeout2的回调函数放入消息队列中的macrotask queue中,此时macrotask queue为[setInterval, setTimeout2]
microtask为空,执行栈为空
第三轮执行结果:'start' 'promise1' 'promise2' 'setInterval' 'setTimeout1' 'promise3' 'promise4'

第四轮:

取出macrotask queue中最早的任务,setInterval,执行setInterval,打印出setInterval,释放掉setInterval,此时macrotask queue为[setTimeout2],执行栈为空
将setInterval放入macrotask queue中,此时macrotask queue为[setTimeout2, setInterval]
执行栈为空,microtask queue为空,开始下一轮循环
第四轮执行结果:'start' 'promise1' 'promise2' 'setInterval' 'setTimeout1' 'promise3' 'promise4' 'setInterval'

第五轮

取出macrotask queue 中最早的任务setTimeout2,执行setTimeout2,打印出setTimeout2,此时macrotask queue为[setInterval]
执行promise,调用resolve方法,触发回调,回调函数被存放在microtask queue中,此时microtask queue为[promise5, promise6, clearInterval]
执行消息队里的microtask queue中的所有回调函数,打印出'promise5', 'promise6',清空interval
此时执行栈为空,macrotask queue为空,microtask task为空,任务结束
第五轮执行结果:'start' 'promise1' 'promise2' 'setInterval' 'setTimeout1' 'promise3' 'promise4' 'setInterval' 'setTimeout2' 'promise5' 'promise6'