Eventloop

811 阅读3分钟

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)。

浏览器下js的事件循环机制

由于存在异步操作的关系,而js又是单线程,所以当遇到一个异步事件的时候,js不会一直等待这个事件,而是把事件放到与当前执行栈不同的队列里面,我们称为事件队列,但是事件队列里面的事件不会先立即执行回调,而是等到栈里面的任务全部执行完,然后主线程回去查看事件队列里面是否有任务,然后把事件队列里面的第一个放到执行栈上面执行同步代码,然后在去取事件队列里面的下一个任务

由于异步任务不相同,所以他们执行的优先级也不一样,不同的异步任务被分为两类:微任务(micro task)和宏任务(macro task)。 其中,微任务有:new Promise()[await需要将其转换成promise],new MutaionObserver(),宏任务有:setInterval(),setTimeout() ajax 和 DOM事件

setTimeout(function () {
   console.log(4);
}, 0);
new Promise(function (resolve) {
   console.log(1);
   resolve();
   console.log(2);
}).then(function () {
   console.log(5);
});

console.log(3);

首先从上往下执行,setTimeout会被先被放到宏任务中,然后执行new Promise,new Promise内部的函数除非执行到resolve(),不然都是同步的,所以会先打印1和2,然后把resolve()放到微任务中,然后打印3,接下来会先执行微任务,所以先打印5,然后执行宏任务,打印4,所以结果是1,2,3,5,4

注意如果有await那么他后面的全部转化到resolve里面

 async function async1() {
     console.log(1);
     await async2();
     console.log(2);
 }
 async function async2() {
     console.log(3)
 }

 async1();

 new Promise(function (resolve) {
     console.log(4);
     resolve();
 }).then(function () {
     console.log(5);
 });

这道题首先执行async1()->打印1->await async2();console.log(2) 转化微 promise.resolve(async2()).then(console.log(2))->打印3->执行new Promise->打印4->执行console.log(2)->打印2->打印5

nodejs的事件循环机制

node中的事件循环存在于libuv引擎中 顺序为:外部输入数据-->轮询阶段(poll)[或从timers进入]-->检查阶段(check)-->关闭事件回调阶段(close callback)-->定时器检测阶段(timer)-->I/O事件回调阶段(I/O callbacks)-->闲置阶段(idle, prepare)-->轮询阶段..

其中在timers这个阶段执行的回调函数有setInterval()setTimeout()

在check这个阶段的有setImmediate()

process.nextTick()是在每一个阶段快要执行完进入下一个阶段开始前执行的

setTimeout(() => {
    console.log('timeout');
}, 0);

setImmediate(() => {
    console.log('immediate');
});

但是若想判断setTimeout和setImmediate谁先执行,这个答案是不一定,因为随着进如的时机不同,有可能会从timers进入或从poll进入,那么这两个谁先谁后就不太好说了,但是如果在他们的外面加上一个回调函数

setTimeout(()=>{
setTimeout(() => {
    console.log('timeout');
}, 0);

setImmediate(() => {
    console.log('immediate');
});
},0)

那么就一定是setImmediate->setTimeout