热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

浏览器的eventloop和node的eventloop

1.什么是eventloopeventloops也就是事件循环,它是为了协调事件(event),用户交互(u

1.什么是event loop

event loops也就是事件循环,它是为了协调事件(event),用户交互(user interaction),脚本(script),渲染(rendering),网络(networking),用户代理(user agent)的工作而产生的一个机制。


2.Javascript的运行机制


2.1 单线程的Javascript

Javascript语言的一大特点就是单线程,也就是说在同一时间只做同一件事。这是基于js的执行环境决定的,因为在浏览器中,有许多的dom操作,如果在同一时间操作一个dom,很容易造成混乱,所以为了避免发生同一时间操作同一dom的情况,js选择只用一个主线程执行代码,来保证程序执行的一致性,单线程的特点也应用到了node中。


2.2 Javascript中的任务和队列

Javascript是单线程的,也就意味着所有任务需要排队,前一个任务执行完,才能执行下一个任务,但是因为IO设备(输入输出设备)很慢(比如Ajax从网络读取数据),不得不等待结果返回之后才能继续,这样的执行效率很慢。
于是分成了两种任务来处理,同步任务和异步任务。
同步任务是指在主线程排队的任务,只有前面的任务执行完之后才执行后面的任务。
异步任务指的是任务不进入主线程,而进入到一个任务队列(task queue),主线程的任务可以继续往后执行,而在任务队列里的异步任务执行完会通知主线程。


3.浏览器的event loop


3.1执行栈与事件队列

当Javascript代码执行的时候会将不同的变量存于内存中的不同位置:堆(heap)和栈(stack)中来加以区分。其中,堆里存放着一些对象。而栈中则存放着一些基础类型变量以及对象的指针。当所有所有同步任务都在主线程上执行时,这些任务被排列在一个单独的地方,形成一个执行栈

当浏览器js引擎解析这段代码时,会将同步任务顺序加入执行栈中依次执行,当遇到异步任务时并不会一直等待异步任务返回结果再执行后面的任务,而是将异步任务挂起,继续执行同步任务,当异步任务返回结果时,将异步任务的回调事件加入到一个事件队列(Task Queue)当中去,这个事件队列里的任务并不会立即执行,而是等同步任务全部执行完,再依次执行事件队列里的事件。

依次执行同步任务,完成后依次执行事件队列,完成后再去执行同步任务,这样形成了一个循环,就是事件循环(Event Loop)。


3.2宏任务(macro task)与微任务(micro task)

异步任务又分为宏任务与微任务两种,微任务并不是老老实实的按照事件队列的顺序去执行,而是按照microTask—>macroTask的顺序去执行,先执行完队列中所有的microTask再去执行macroTask

宏任务和微任务的分类


  • MacroTask: script(整体代码), setTimeout, setInterval, setImmediate(node独有), I/O, UI rendering

  • MicroTask: process.nextTick(node独有), Promises, Object.observe(废弃), MutationObserver

举个例子
setTimeout(()=>{console.log(1)
})
Promise.resolve().then(function() {console.log(2)
})

console.log(3)执行结果是:3 2 1
这是因为事件循环的顺序是:同步代码=>
微任务=>宏任务

4.node的event loop


  • timers: 这个阶段执行定时器队列中的回调如 setTimeout() 和 setInterval()。

  • I/O callbacks: 这个阶段执行几乎所有的回调。但是不包括close事件,定时器和setImmediate()的回调。

  • idle, prepare: 这个阶段仅在内部使用,可以不必理会。

  • poll: 等待新的I/O事件,node在一些特殊情况下会阻塞在这里。

  • check: setImmediate()的回调会在这个阶段执行。

  • close callbacks: 例如socket.on(‘close’, …)这种close事件的回调。


event loop的每一次循环都需要依次经过上述的阶段。 每个阶段都有自己的callback队列,每当进入某个阶段,都会从所属的队列中取出callback来执行,当队列为空或者被执行callback的数量达到系统的最大数量时,进入下一阶段。这六个阶段都执行完毕称为一轮循环。

举个例子(1)
浏览器与Node执行顺序的区别
setTimeout(()=>{console.log('timer1')Promise.resolve().then(function() {console.log('promise1')})
})
setTimeout(()=>{console.log('timer2')Promise.resolve().then(function() {console.log('promise2')})
})

浏览器输出:
time1
promise1
time2
promise2
因为promise是microtask,所以当第一个setTimeout执行完之后,先执行promise。

Node输出:
time1
time2
promise1
promise2
因为time1和time2都在timers阶段,所以先执行timers,promise的回调被加入到了microtask队列,等到timers阶段执行完毕,在去执行microtask队列。

举个例子(2)
MicroTask队列与MacroTask队列
setTimeout(function () {console.log(1);
});
console.log(2);
process.nextTick(() => {console.log(3);
})
;
new Promise(function (resolve, rejected) {console.log(4);resolve()
})
.then(res=>{console.log(5);
})

setImmediate(function () {console.log(6)
})

console.log('end');

node输出的顺序是
2
4
end
3
5
1
6
首先执行的是同步任务中的2 4 end,然后是microTask队列中的process.nextTick:3、promise.then:5,最后是macroTask队列中的setTimeout:1、setImmediate:6,由于Timer优于Check阶段,所以先1后6。


推荐阅读
  • C++ 异步编程中获取线程执行结果的方法与技巧及其在前端开发中的应用探讨
    本文探讨了C++异步编程中获取线程执行结果的方法与技巧,并深入分析了这些技术在前端开发中的应用。通过对比不同的异步编程模型,本文详细介绍了如何高效地处理多线程任务,确保程序的稳定性和性能。同时,文章还结合实际案例,展示了这些方法在前端异步编程中的具体实现和优化策略。 ... [详细]
  • 检查在所有可能的“?”替换中,给定的二进制字符串中是否出现子字符串“10”带 1 或 0 ... [详细]
  • 本文将继续探讨 JavaScript 函数式编程的高级技巧及其实际应用。通过一个具体的寻路算法示例,我们将深入分析如何利用函数式编程的思想解决复杂问题。示例中,节点之间的连线代表路径,连线上的数字表示两点间的距离。我们将详细讲解如何通过递归和高阶函数等技术实现高效的寻路算法。 ... [详细]
  • 本文详细探讨了使用纯JavaScript开发经典贪吃蛇游戏的技术细节和实现方法。通过具体的代码示例,深入解析了游戏逻辑、动画效果及用户交互的实现过程,为开发者提供了宝贵的参考和实践经验。 ... [详细]
  • Ihavetwomethodsofgeneratingmdistinctrandomnumbersintherange[0..n-1]我有两种方法在范围[0.n-1]中生 ... [详细]
  • javascript分页类支持页码格式
    前端时间因为项目需要,要对一个产品下所有的附属图片进行分页显示,没考虑ajax一张张请求,所以干脆一次性全部把图片out,然 ... [详细]
  • 重要知识点有:函数参数默许值、盈余参数、扩大运算符、new.target属性、块级函数、箭头函数以及尾挪用优化《深切明白ES6》笔记目次函数的默许参数在ES5中,我们给函数传参数, ... [详细]
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 第二十五天接口、多态
    1.java是面向对象的语言。设计模式:接口接口类是从java里衍生出来的,不是python原生支持的主要用于继承里多继承抽象类是python原生支持的主要用于继承里的单继承但是接 ... [详细]
  • 在JavaWeb开发中,文件上传是一个常见的需求。无论是通过表单还是其他方式上传文件,都必须使用POST请求。前端部分通常采用HTML表单来实现文件选择和提交功能。后端则利用Apache Commons FileUpload库来处理上传的文件,该库提供了强大的文件解析和存储能力,能够高效地处理各种文件类型。此外,为了提高系统的安全性和稳定性,还需要对上传文件的大小、格式等进行严格的校验和限制。 ... [详细]
  • 在PHP中如何正确调用JavaScript变量及定义PHP变量的方法详解 ... [详细]
  • Python 伦理黑客技术:深入探讨后门攻击(第三部分)
    在《Python 伦理黑客技术:深入探讨后门攻击(第三部分)》中,作者详细分析了后门攻击中的Socket问题。由于TCP协议基于流,难以确定消息批次的结束点,这给后门攻击的实现带来了挑战。为了解决这一问题,文章提出了一系列有效的技术方案,包括使用特定的分隔符和长度前缀,以确保数据包的准确传输和解析。这些方法不仅提高了攻击的隐蔽性和可靠性,还为安全研究人员提供了宝贵的参考。 ... [详细]
  • MATLAB字典学习工具箱SPAMS:稀疏与字典学习的详细介绍、配置及应用实例
    SPAMS(Sparse Modeling Software)是一个强大的开源优化工具箱,专为解决多种稀疏估计问题而设计。该工具箱基于MATLAB,提供了丰富的算法和函数,适用于字典学习、信号处理和机器学习等领域。本文将详细介绍SPAMS的配置方法、核心功能及其在实际应用中的典型案例,帮助用户更好地理解和使用这一工具箱。 ... [详细]
  • 在本地环境中部署了两个不同版本的 Flink 集群,分别为 1.9.1 和 1.9.2。近期在尝试启动 1.9.1 版本的 Flink 任务时,遇到了 TaskExecutor 启动失败的问题。尽管 TaskManager 日志显示正常,但任务仍无法成功启动。经过详细分析,发现该问题是由 Kafka 版本不兼容引起的。通过调整 Kafka 客户端配置并升级相关依赖,最终成功解决了这一故障。 ... [详细]
author-avatar
门前西瓜飘乐
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有