JavaScript学习笔记(三十四)——Event Loop

什么是Event Loop?

Event Loop 是一个很重要的概念,指的是计算机系统的一种运行机制,决定了同步代码和异步代码的执行方式。

JavaScript语言就采用这种机制,来解决单线程运行带来的一些问题。

在这里插入图片描述
Event Loop在不同的地方有不同的实现,浏览器和Node.JS基于不同的技术实现了各自的Event Loop。

  • 浏览器的Event Loop是在HTML5的规范中明确定义。
  • Node.JS的Event Loop是基于libuv实现的。
  • libuv已经对Event Loop作出了实现,而HTML5规范中只是定义了浏览器中的Event Loop模型,具体的实现留给了浏览器厂商。

执行栈(调用栈)

Javascript 有一个 main thread 主线程和 call-stack 调用栈(执行栈),所有的任务都会被放到调用栈等待主线程执行。

JS调用栈采用的是后进先出的规则,当函数执行的时候,会被添加到栈的顶部,当执行栈执行完成后,就会从栈顶移出,直到栈内被清空。

宏任务

  • setTimeout
  • setInterval
  • setImmediate(Node独有)
  • requestAnimationFrame(浏览器独有)
  • I/O
  • UI rendering(浏览器独有)

微任务

  • process.nextTick(Node独有)
  • Promise
  • Object.observe
  • MutationObserver

代码执行顺序:

  1. 执行全局JS同步代码,这些同步代码中有一些是同步语句,有一些是异步语句
  2. 全局Script代码执行完毕后,执行栈清空
  3. 从微任务队列中取出位于队首的任务,放入到执行栈中执行,该任务执行完毕后,继续从微任务队列中取出下一个任务,直到微任务队列清空
  4. 从宏任务队列中取出位于队首的任务,放入到执行栈中执行,该任务执行完毕后,继续从宏任务队列中取出下一个任务,直到宏任务队列清空
    注意: 如果在执行第3步时,遇到宏任务,则将任务追加到宏任务队列的队尾,继续执行微任务队列里面的任务;如果在执行第4步时,遇到微任务,则将任务放到微任务队列,微任务队列将该任务放到执行栈中执行,直到微任务队列清空,再继续返回来执行宏任务
  5. 全部执行完毕后,执行栈为空

例子1:

		// 1、循环输出0~9,此时i=10
        let i;
        for (i = 0; i < 10; i++){
            console.log(i);
        }
        // 2、开启延时器,先将其放到window上,过1000毫秒后将其放入宏任务队列
        // 5、执行栈清空后,由于微任务队列中没有任务,所以直接来执行宏任务队列里面的任务,将位于队首的任务放到执行栈中执行,延时1000毫秒后,输出20
        setTimeout(function(){
            console.log(i);            
        }, 1000)
        // 3、开启延时器,先将其放到window上,过2000毫秒后将其放入宏队列
        // 6、上一个任务执行结束后,将该任务放到执行栈中执行,延时2000毫秒后,输出hello
        setTimeout(function(){
            console.log("hello");
        }, 2000)
        // 4、循环开始时i=10,循环输出10~19,此时i=20,执行栈清空
        for (; i < 20; i++){
            console.log(i);
        }

最后输出的顺序为:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 hello

例子2:

        // 1、定义一个变量并赋值为1
        let a = 1;
        // 2、开启定时器,并将其放到windon上,过1000毫秒后将其放入宏任务队列
        setTimeout(function(){
            // 11、微任务队列清空后,执行宏任务里面的任务,将任务放到执行栈中执行,延时1000毫秒后,输出1
            console.log(a);
        }, 1000);
        // 3、输出2
        console.log(2);
        // 4、初始化一个Promise实例
        let p = new Promise(function(resolve, reject){
            // 5、输出3
            console.log(3);
            // 6、状态变为成功
            resolve();
        });
        // 7、输出4
        console.log(4);
        // 8、绑定then,因为状态已经发生变化,所以立即将这个函数放入微任务队列
        p.then(function(){
            // 10、执行栈清空后,执行微任务队列里面的任务,将任务放到执行栈中执行,输出5,此时微任务队列清空
            console.log(5);
        });
        // 9、输出6,此时执行栈清空
        console.log(6);

最后输出顺序为:2 3 4 6 5 1

例子3:

        // 1、输出1
        console.log(1);
        // 2、开启定时器,将其放到window上,过0毫秒后将其放入宏任务队列
        setTimeout(function() {
            // 14、输出2
            console.log(2)
        }, 0)
        // 3、初始化一个Promise实例
        new Promise(function(resolve) {
            // 4、输出3
            console.log(3)
            // 5、状态变为成功
            resolve();
            // 6、绑定then,因为状态已经发生变化,所以立即将这个函数放入到微任务队列
        }).then(function() {
            // 11、输出4
            console.log(4)
            // 12、开启定时器,将其放到window上,过0毫秒后将其放入宏任务队列
            setTimeout(function() {
                // 15、输出5
                console.log(5)
            }, 0)
        })
        // 7、输出6
        console.log(6);
        // 8、初始化一个Promise实例
        new Promise(function(resolve, reject) {
            // 9、状态变为失败
            reject()
        })
        .then(function() {
        })
        // 10、绑定catch,因为状态已经发生变化,所以立即将这个函数放入到为任务队列,此时执行栈清空
        .catch(function() {
            // 13、开启定时器,将其放到window上,过0毫秒后将其放入宏任务队列,此时微任务队列清空
            setTimeout(function() {
                // 16、输出7
                console.log(7)
            }, 0)
        })

最后输出顺序为:1 3 6 4 2 5 7

例子4:

        // 1、输出1
        console.log(1);
        // 2、开启定时器,将其放到window上,过0毫秒后将其放到宏任务队列
        setTimeout(function(){
            // 10、输出2
            console.log(2);
            // 11、初始化一个Promise实例
            new Promise(function(resolve, reject){
                // 12、状态变为成功
                resolve();
                // 13、绑定then,因为状态已经发生变化,所以立即将这个函数放入到微任务队列
            }).then(function(){
                // 14、输出3
                console.log(3);
            })
        }, 0);
        // 3、初始化一个Promise实例
        new Promise(function(resolve, reject){
            // 4、输出4
            console.log(4);
            // 5、状态变为成功
            resolve(5);
            // 6、绑定then,因为状态已经发生变化,所以立即将这个函数放入到微任务队列
        }).then(function(data){
            // 9、输出5
            console.log(data);
        });
        // 7、开启定时器,将其放到window上,过0毫秒后将其放到宏任务队列
        setTimeout(function(){
            // 15、输出6
            console.log(6);
        }, 0)
        // 8、输出7,此时执行栈清空
        console.log(7);

最后输出顺序为:1 4 7 5 2 3 6

例子5:

        // 1、输出1
        console.log(1);
        // 2、开启定时器,将其放到window上,过0毫秒后将其放到宏任务队列
        setTimeout(function(){
            // 16、输出2
            console.log(2);
            // 17、初始化一个Promise实例
            new Promise(function(resolve, reject){
                // 18、状态变为成功
                resolve();
                // 19、绑定then,因为状态已经发生变化,所以立即将这个函数放入到微任务队列
            }).then(function(){
                // 20、输出3
                console.log(3)
            })
        }, 0);
        // 3、初始化一个Promise实例
        new Promise(function(resolve, reject){
            // 4、输出4
            console.log(4);
            // 5、状态变为成功
            resolve(5);
            // 6、绑定then,因为状态已经发生变化,所以立即将这个函数放入到微任务队列
        }).then(function(data){
            // 9、输出5
            console.log(data);
            // 10、初始化一个Promise实例
            new Promise(function(resolve, reject){
                // 11、输出6
                console.log(6);
                // 12、状态变为成功
                resolve();
                // 13、绑定then,因为状态已经发生变化,所以立即将这个函数放入到微任务队列
            }).then(function(){
                // 14、输出7
                console.log(7);
                // 15、开启定时器,将其放到window上,过0毫秒后将其放到宏任务队列
                setTimeout(function(){
                    // 22、输出8
                    console.log(8);
                }, 0);
            })
        });
        // 7、开启定时器,将其放到window上,过0毫秒后将其放到宏任务队列
        setTimeout(function(){
            // 21、输出9
            console.log(9);
        }, 0);
        // 8、输出10,此时执行栈清空
        console.log(10);

最后输出顺序为:1 4 10 5 6 7 2

©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页