事件循环(Event Loop)总结

事件循环(Event Loop)

浏览器事件循环

事件循环是浏览器的机制,咱们先了解一下浏览器工作过程:
浏览器是多进程的,其中的渲染进程有多个线程在工作:GUI 渲染线程(负责渲染页面,解析 HTML,CSS 构成 DOM 树等、JS 引擎线程(负责解析运行 js 脚本)、事件触发线程(DOM Binding模块)、定时器触发线程(Timer模块)、http 请求线程(NetWork模块)

js引擎是单线程,也就是只有一个主线程,任务按照顺序等待执行,每次只能执行一项。要执行的任务分为2种:同步和异步。同步是指在主线程排队等待执行,异步是指不进入主线程。
常见到的异步任务:定时器会被Timer模块处理、Dom绑定的事件例如onclick会被DOM Binding模块处理,http请求会被NetWork模块处理,这些异步任务有了运行结果之后,将其回调放入任务队列中,等待主线程空闲的时候,再读取任务队列。

任务队列也分为2种:宏任务和微任务。定时器、事件绑定、http请求运行后的回调会放入宏任务队列中,Promise、nextTick运行后的回调会放入到微任务对象中。整段js代码的同步任务属于一次宏任务

执行过程:
1、主线程执行,形成一个执行栈,执行所有的同步任务
2、遇到异步任务,则留给对应的线程处理,处理后的回调放入到对应的任务队列中。
3、执行栈中的同步任务执行完之后,读取微任务队列进入执行栈,执行所有。这是一轮事件循环。
4、微任务队列清空后后,读取宏任务队列的一个任务进入到执行栈,执行(注意:1个)
5、1个宏任务执行完之后,读取微任务队列进入执行栈,执行所有
6、然后继续执行4、5、4、5 一直到所有任务执行完,结束事件循环
注:执行任务队列期间遇到的异步,相应的线程处理后的回调也要添加到对应的任务队列中。

总结:主线程从"任务队列"中读取事件,这个过程是循环完成的,所以这种运行机制称为Event Loop(事件循环)

接下来是个简单的小练习:

console.log(1);
setTimeout(function() {
    console.log(2);
})
var promise = new Promise(function(resolve, reject) {
    console.log(3);
    resolve();
})
promise.then(function() {
    console.log(4);
})
console.log(5);
// 1 3 5 4 2
1、主进程执行同步任务 打印1、3、5 。注意new Promise是正常函数调用,属于同步任务,执行了resolve之后,其回调函数then会进入到微任务队列等待,定时器的回调函数会进入到宏任务队列等待。
2、同步任务执行完之后,先执行所有微任务,打印4,
3、微任务队列清空后,执行一次宏任务(宏任务队列中的第一个) 打印2
4、然后检查微任务队列,没有任务,继续读取宏任务队列,也没有任务,故结束事件循环 

接下来上一个复杂点的:

console.log('script start');
setTimeout(function() {
    console.log('timeout1');
    setTimeout(function() {
        console.log('timeout2');
    }, 10);
    new Promise(resolve => {
        console.log('promise1');
        resolve();
    }).then(()=>{
        console.log('then1')
    })
}, 10);
new Promise(resolve => {
    console.log('promise2');
    resolve();
    setTimeout(() => console.log('timeout3'), 10);
}).then(function() {
    console.log('then2')
})
console.log('script end');
//script start
//promise2
//script end
//then2
//timeout1
//promise1
//then1
//timeout3
//timeout2

执行过程:
1、主线程生成的执行栈先执行同步任务,打印 **script start**
2、定时器1处理后的回调进入宏任务队列等待
3、promise2正常函数调用,打印 **promise2**,其中的定时器3 等待处理之后的回调进入宏任务队列,排在定时器1后面。后面的then回调进入微任务队列
4、打印**script end**
5、同步任务执行完毕,读取微任务队列 此时微任务队列只有一个打印then2的任务,故打印 **then2** 后,微任务队列就空了
6、读取宏任务队列的第一个任务,打印**timeout1**,其中的timeout2等待处理后的回调进入到宏任务队列,排在定时器3任务后面,promise正常函数调用,打印**promise1** 其中resolve处理之后的回调函数进入微任务队列等待
7、读取微任务,打印**then1**后,微任务队列为空
8、读取宏任务队列当前第一个任务,打印**timeout3**
9、检查微任务队列,为空
10、读取宏任务队列当前第一个任务,打印**timeout2**
11、检查微任务队列、为空
12、检查宏任务队列、为空、结束事件循环

Node事件循环

Node.js 是单进程单线程应用程序,采用V8作为js的解析引擎,该引擎提供的异步执行回调接口可以处理大量的并发。使用libuv处理I/O,libuv 是一个基于事件驱动的跨平台抽象层,封装了不同操作系统一些底层特性,对外提供统一的 API。

运行机制:
1、V8 引擎解析 JavaScript 脚本。
2、解析后的代码,调用 Node API。
3、libuv 库负责 Node API 的执行。它将不同的任务分配给不同的线程,形成一个 Event Loop(事件循环),以异步的方式将任务的执行结果返回给 V8 引擎。
4、V8 引擎再将结果返回给用户

事件循环中每一次循环都有6个阶段:
timers 阶段 执行 timer(setTimeout、setInterval)的回调
I/O callbacks 处理一些上一轮循环中的少数未执行的 I/O 回调
idle, prepare 阶段 仅 node 内部使用
poll 阶段 获取新的 I/O 事件, 适当的条件下 node 将阻塞在这里
check 阶段 执行 setImmediate() 的回调
close callbacks 阶段 执行 socket 的 close 事件回调
这6个阶段按照顺序反复运行,直到没有正在等待的异步I/O 或者定时器,就清除并结束。每当进入某一个阶段的时候,都会从对应的回调队列中取出函数去执行。当队列为空或者执行的回调函数数量到达系统设定的阈值,就会进入下一阶段

日常开发中的绝大部分异步任务都是在timers、poll、check这 3 个阶段处理的

和浏览器的区别,node事件循环中的微任务队列在每个阶段都会执行至清空

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值