一个菜鸡对Event Loop的理解

184 阅读4分钟

(没人看,我也写,哼)

js是单线程执行代码,同一个时间点只能执行一个任务,要理解 Event Loop 首先需要明确两个概念。

1.同步的和异步的概念,同步的是立即执行的,异步是要挂起到任务队列中。

2.宏任务和微任务的概念,宏任务包括 script 代码(整体代码),setTimeout,setInterval,I/O,UI 交互,微任务包括 Promise(then和catch),Object.oberve(好像被废弃了)。 (不说 node.js 环境)

结合这两个概念,来说一下事件执行的先后顺序,所有刚开始就被放入主线程的任务也就是同步执行的可以看做是一个宏任务,执行完第一个宏任务,然后去执行所有的微任务,执行完所有的微任务,再去拿一个宏任务执行

看几个示例(输出结果不写了,自己运行看)

示例1

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

第一个 setTimeout 放入宏任务队列中,然后是 promise,他本身没有代码,如果有的话就是立即执行的,然后是他的 then 推入微任务队列中,最后一行 console 立即执行,然后执行微任务,刚才放进去一个 promise 的 then,执行他,微任务队列没有了,执行宏任务就是刚才的 setTimeout

示例2

    setTimeout(()=>{
        console.log(1)
    },0)
    let a = new Promise((resolve)=>{
        console.log(2)
        resolve()
    }).then(()=>{
        console.log(3)
    }).then(()=>{
        console.log(4)
    })
    console.log(5)

刚开始的 setTimeout 推入宏任务队列中,然后 promise,里面有一行 console,立即执行输出,然后第一个 then 推入微任务队列中,然后是最下面的 console,立即执行输出,然后执行微任务,刚才 promise的第一个 then,执行完后面还有一个 then 跟着推入微任务队列,再执行这个微任务,现在微任务队列中没有了,执行宏任务,刚才的setTimeout 这里要注意的就是在执行微任务的时候,如果遇到新的微任务,是要被立即推入微任务队列的

实例3

    new Promise((resolve,reject)=>{
        console.log("promise1")
        resolve()
    }).then(()=>{
        console.log("then11")
        new Promise((resolve,reject)=>{
            console.log("promise2")
            resolve()
        }).then(()=>{
            console.log("then21")
        }).then(()=>{
            console.log("then23")
        })
    }).then(()=>{
        console.log("then12")
    })

这个就比较复杂了,最外面的 promise ,刚开始的console立即执行, 然后他的第一个 then 推入微任务队列中, 然后出队,执行, 刚开始的 console,然后执行里面的 promise21 的 console,然后将 promise 的第一个 then 推入任务队列, 然后回到最外面的 promise1,他的第二个 then 推入队列, 接着 promise2 的第一个 then 出队执行,把它的第二个 then 推入任务队列, 接着执行刚才推入队列中的 promise1 的第二个 then,然后刚才入队的 promise2 的第二个then出队,执行

这里要注意的是连着的 then 并不是一起被推入微任务队列的,而是后面的 then 一定是等前面的 then 执行过才把后面的 then 推入任务队列的 可以实验一下

    new Promise(reslove => {
        console.log('p1')
        reslove()
    }).then(res => {
        console.log('t1')
    }).then(res => {
        console.log('t2')
    })

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

如果是连着的then是一起被推入队列的那么输出就应该是p1 p2 t1 t2 t3 但是输出的是p1 p2 t1 t3 t2 还有就是 promise 里的第一个 then 一定是跟着就被推入队列中的,这么想的话,就清晰明了了,不管它里面再嵌套几层都是这个道理

示例3的变异版

    new Promise((resolve,reject)=>{
        console.log("promise1")
        resolve()
    }).then(()=>{
        console.log("then11")
        return new Promise((resolve,reject)=>{
            console.log("promise2")
            resolve()
        }).then(()=>{
            console.log("then21")
        }).then(()=>{
            console.log("then23")
        })
    }).then(()=>{
        console.log("then12")
    })

如果里面的 promise 直接整个被 return 了呢,现在 then12 其实是被挂在 promise2 最后一个 then 上的,简单点,他就是 promise2 的第三个 then(暂且先这么理解),所以现在输出结果是 promise1 then11 promise2 then21 then23 then12

示例4

    async function async1() {
        console.log("async1 start");
        await  async2();
        console.log("async1 end");
    }

    async  function async2() {
        console.log( 'async2');
    }

    console.log("script start");

    setTimeout(function () {
        console.log("settimeout");
    },0);

    async1();

    new Promise(function (resolve) {
        console.log("promise1");
        resolve();
    }).then(function () {
        console.log("promise2");
    });
    console.log('script end');

async await不知道的自行查资料。来,走一遍 前面两个函数声明不说,第一个 console.log 执行,输出 ‘script start’,然后 timeout 放入宏任务队列,然后 async1 被执行,执行里面第一行 console,输出‘async1 start’后面的 await 相当于是一个 promise,后面跟着的相当于是 promise 里面的代码,所以直接执行,接着执行 async2 里面的代码,输出‘async2’,然后回到 async,再之后的代码相当于是 promise 的 then,所以放入微任务队列中,接着走 promise,先执行里面的 console,输出‘promise1’,后面跟着的 then 放入微任务队列,接着最后一个 console,输出‘script end’,然后执行微任务,async1 里面最后的 console,执行输出‘async end’,然后 promise 的 then 里面的console,输出‘promise2,微任务完了,还有一个宏任务,执行,输出‘setTimeout’

这里要注意的就是await那,他其实相当于是

    new promise(resolve => {
        async2()
        resolve()
    }).then(
        console.log('...')
    )

所以 按着 promise 的思路走就行了。

完了,欢迎大佬们指教和指正。