nodejs--事件环、宏任务和微任务

目录

一、事件环

1.1 初识事件环

1.2 NodeJS事件环和浏览器事件环区别

1.3 NodeJS中的任务队列

1.4 其他

二、宏任务和微任务

2.1 初识宏任务和微任务

2.2 宏任务和微任务

2.3 常见的宏任务和微任务

2.4 注意点:

 


 

一、事件环

1.1 初识事件环

和浏览器中一样NodeJS中也有事件环(Event Loop),但是由于执行代码的宿主环境和应用场景不同,所以两者的事件环也有所不同

1.2 NodeJS事件环和浏览器事件环区别

任务队列个数不同:

浏览器事件环有2个事件队列(宏任务队列和微任务队列)

NodeJS事件环有6个事件队列

微任务队列不同,

  • 浏览器事件环中有专门存储微任务的队列
  • NodeJS事件环中没有专门存储微任务的队列

微任务执行时机不同,

  • 浏览器事件环中每执行完一个宏任务都会去清空微任务队列
  • NodeJS事件环中只有同步代码执行完毕和其它队列之间切换的时候回去清空微任务队列

微任务优先级不同,

  • 浏览器事件环中如果多个微任务同时满足执行条件, 采用先进先出
  • NodeJS事件环中如果多个微任务同时满足执行条件, 会按照优先级执行

示例:

Promise.resolve().then(function() {
    console.log("Promise");
});
process.nextTick(function() {
    console.log("process.nextTick1");
});
process.nextTick(function() {
    console.log("process.nextTick2");
});
process.nextTick(function() {
    console.log("process.nextTick3");
});


1.3 NodeJS中的任务队列

    ┌───────────────────────┐

┌> │timers          │执行setTimeout() 和 setInterval()中到期的callback

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

│  │pending callbacks│执行系统操作的回调, 如:tcp, udp通信的错误callback

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

│  │idle, prepare   │只在内部使用

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

│  │poll            │执行与I/O相关的回调

    │                  (除了close回调、定时器回调和setImmediate()之外,几乎所有回调都执行);

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

│  │check           │执行setImmediate的callback

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

└─┤close callbacks │执行close事件的callback,例如socket.on("close",func)

    └───────────────────────┘


 

    ┌───────────────────────┐

┌> │timers          │执行setTimeout() 和 setInterval()中到期的callback

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

│  │poll            │执行与I/O相关的回调

    │                  (除了close回调、定时器回调和setImmediate()之外,几乎所有回调都执行);

│  └──────────┬────────────┘

│  ┌──────────┴────────────┐

└─┤check           │执行setImmediate的callback

    └───────────────────────┘

注意点:

  • 和浏览器不同的是没有宏任务队列和微任务队列的概念
  • 宏任务被放到了不同的队列中, 但是没有队列是存放微任务的队列
  • 微任务会在执行完同步代码和队列切换的时候执行

什么时候切换队列?

当队列为空(已经执行完毕或者没有满足条件回到)

或者执行的回调函数数量到达系统设定的阈值时任务队列就会切换

注意点:

在NodeJS中process.nextTick微任务的优先级高于Promise.resolve微任务

            ┌───────────────────────┐

            │                同步代码

            └──────────┬────────────┘

                                  │

                                  │ <---- 满足条件微任务代码

                                  │

            ┌──────────┴────────────┐

        ┌> │timers          │执行setTimeout() 和 setInterval()中到期的callback

        │  └──────────┬────────────┘

        │                        │

        │                        │ <---- 满足条件微任务代码

        │                        │

        │  ┌──────────┴────────────┐

        │  │poll            │执行与I/O相关的回调

        │  │                  (除了close回调、定时器回调和setImmediate()之外,几乎所有回调都执行);

        │  └──────────┬────────────┘

        │                        │

        │                        │ <---- 满足条件微任务代码

        │                        │

        │  ┌──────────┴────────────┐

        └─┤check           │执行setImmediate的callback

            └───────────────────────┘

示例 :

setTimeout(function() {
    console.log("setTimeout");
});

Promise.resolve().then(function() {
    console.log("Promise");
});

console.log("同步代码 Start");

process.nextTick(function() {
    console.log("process.nextTick");
});

setImmediate(function() {
    console.log("setImmediate");
});
console.log("同步代码 End");

执行流程,

注意点:

  • 执行完poll, 会查看check队列是否有内容, 有就切换到check
  • 如果check队列没有内容, 就会查看timers是否有内容, 有就切换到timers
  • 如果check队列和timers队列都没有内容, 为了避免资源浪费就会阻塞在poll

示例,

setTimeout(function() {
    console.log("setTimeout1");
    // p1
    Promise.resolve().then(function() {
        console.log("Promise1");
    });
    // n1
    process.nextTick(function() {
        console.log("process.nextTick1");
    });
});
console.log("同步代码 Start");
setTimeout(function() {
    console.log("setTimeout2");
    // p2
    Promise.resolve().then(function() {
        console.log("Promise2");
    });
    // n2
    process.nextTick(function() {
        console.log("process.nextTick2");
    });
});
console.log("同步代码 End");

执行流程:


1.4 其他

注意点: 如下代码输出的结果是随机的

        在NodeJS中指定的延迟时间是有一定的误差的, 所以导致了输出结果随机的问题

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


二、宏任务和微任务

2.1 初识宏任务和微任务

  JS是单线程的:JS中的代码都是串行的, 前面没有执行完毕后面不能执行

执行顺序:

  • 程序运行会从上至下依次执行所有的同步代码
  • 在执行的过程中如果遇到异步代码会将异步代码放到事件循环中
  • 当所有同步代码都执行完毕后, JS会不断检测 事件循环中的异步代码是否满足条件
  • 一旦满足条件就执行满足条件的异步代码


2.2 宏任务和微任务

在JS的异步代码中又区分"宏任务(MacroTask)"和"微任务(MicroTask)"

宏任务(MacroTask)宏/大的意思, 可以理解为比较费时比较慢的任务

微任务(MicroTask)微/小的意思, 可以理解为相对没那么费时没那么慢的任务

2.3 常见的宏任务和微任务

i.宏任务 (MacroTask):

setTimeout, setInterval, setImmediate(IE独有)...

setlmmediate的基本用法:

setImmediate(function() {
            console.log("setImmediate");
        });
console.log("同步代码Start");
console.log("同步代码End");

setImmediate和setTimeout, setInterval区别:

setImmediate不能设置延迟时间, 并且只能执行一次;IE独有,也就是只能IE浏览器里才能生效

ii. 微任务(MicroTask)

Promise, MutationObserver ,process.nextTick(node独有) ...

MutationObserver的基本用法:

MutationObserver是专门用于监听节点的变化

<div></div>
<button class="add">添加节点</button>
<button class="del">删除节点</button>
        let oDiv = document.querySelector("div");
        let oAddBtn = document.querySelector(".add");
        let oDelBtn = document.querySelector(".del");
        oAddBtn.onclick = function() {
            let op = document.createElement("p");
            op.innerText = "我是段落";
            oDiv.appendChild(op);
        }
        oDelBtn.onclick = function() {
            let op = document.querySelector("p");
            oDiv.removeChild(op);
        }

        //监听--只要监听到了你添加或者是删除节点就会打印如下的语句
        let mb = new MutationObserver(function() {
            console.log("执行了");
        });

        //选择监听的对象
        // 这里表示监听oDiv子节点的变化
        mb.observe(oDiv, {
            "childList": true
        });

        console.log("同步代码Start");
        console.log("同步代码End");


2.4 注意点:

  • 所有的宏任务和微任务都会放到自己的执行队列中, 也就是有一个宏任务队列和一个微任务队列
  • 所有放到队列中的任务都采用"先进先出原则", 也就是多个任务同时满足条件, 那么会先执行先放进去的

完整执行顺序:

  • 从上至下执行所有同步代码
  • 在执行过程中遇到宏任务就放到宏任务队列中,遇到微任务就放到微任务队列中
  • 当所有同步代码执行完毕之后, 就执行微任务队列中满足需求所有回调
  • 当微任务队列所有满足需求回调执行完毕之后, 就执行宏任务队列中满足需求所有回调
  • ... ...

示例1,

        // 1.定义一个宏任务
        setTimeout(function() {
            console.log("setTimeout1");
        }, 0);

        // 2.定义一个微任务
        Promise.resolve().then(function() {
            console.log("Promise1");
        });

        console.log("同步代码Start");

        Promise.resolve().then(function() {
            console.log("Promise2");
        });

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

        console.log("同步代码End");

执行流程:

注意点:

每执行完一个宏任务都会立刻检查微任务队列有没有被清空, 如果没有就立刻清空

示例2,

        // 1.定义一个宏任务
        setTimeout(function() {
            console.log("setTimeout1");
            // 2.定义一个微任务 p1
            Promise.resolve().then(function() {
                console.log("Promise1");
            });
            // 2.定义一个微任务 p2
            Promise.resolve().then(function() {
                console.log("Promise2");
            });
        }, 0);

        // 1.定义一个宏任务
        setTimeout(function() {
            console.log("setTimeout2");
            // 2.定义一个微任务 p3
            Promise.resolve().then(function() {
                console.log("Promise3");
            });
            // 2.定义一个微任务 p4
            Promise.resolve().then(function() {
                console.log("Promise4");
            });
        }, 0);

执行流程:

示例3,

         // 1.定义一个宏任务
        setTimeout(function() {
            console.log("setTimeout1");
            // 2.定义一个微任务 p2
            Promise.resolve().then(function() {
                console.log("Promise2");
            });
            // 2.定义一个微任务 p3
            Promise.resolve().then(function() {
                console.log("Promise3");
            });
        }, 0);
        // 2.定义一个微任务 p3
        Promise.resolve().then(function() {
            console.log("Promise1");
            // s2
            setTimeout(function() {
                console.log("setTimeout2");
            });
            // s3
            setTimeout(function() {
                console.log("setTimeout3");
            });
        });

执行流程:


版权声明:本文为weixin_43285360原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。