Node.js事件驱动_node.js
Node.js事件驱动实现概览
虽然在ECMAScript的标准里并没有(也没有必要)明确规定“事件”,但是在浏览器中,事件作为一个极为重要的机制,给予JavaScript响应用户操作与DOM变化的能力;在Node.js中,异步事件驱动模型则是其高并发能力的基础。
学习JavaScript也需要了解它的运行平台,为了更好的理解JavaScript的事件模型,我打算从Node及浏览器引擎源码入手,分析其底层实现,并将我的分析整理为一系列博文;一方面作为笔记,另一方面也希望能与大家交流,分析和理解有疏漏偏颇之处,还望各位斧正。
简述事件驱动模型
解释JavaScript事件模型本身的好文章已经很多了,可以说这已经是一个说烂了的话题,这里我只简单写一下,并且提供一些好文章的链接。
程序如何响应事件
我们的程序响应外部的事件有如下两种方式:
中断
操作系统处理键盘等硬件输入就是通过中断来进行的,这个方式的好处是即使没有多线程,我们也可以放心地执行我们的代码,CPU收到中断信号之后自动地转去执行相应的中断处理程序,处理完成后会恢复原来的代码的执行环境继续执行。这种方式需要硬件的支持,一般来说都会被操作系统封装起来。
轮询
循环检测是否有事件发生,如果有就去执行相应的处理程序。这在底层和上层的开发中都有应用。
Windows窗口程序就需要在主线程中写下如下代码,通常称做消息循环:
MSG msg = { }; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }
消息循环不断检测是否有消息(用户的UI操作、系统消息等)出现,有的话就分发消息,调用相应的回调函数进行处理。
轮询方式的一个缺点就是:如果在主线程的消息循环里进行耗时操作,程序就无法及时响应新的消息。这在JavaScript中表现明显,以后还会提到这一点,并探讨其解决方案。
然而JavaScript中并没有类似消息循环代码,我们只是简单地注册事件,然后等待被调用。这是因为浏览器、Node作为执行平台,已经将event loop实现了,JavaScript代码不需要介入到这个过程中,只需要作为被调用者安静地等待即可。
Node中的event loop
通过Node源码看event loop的实现
Node采用V8作为JavaScript的执行引擎,同时使用libuv实现事件驱动式异步I/O。其事件循环就是采用了libuv的默认事件循环。
在src/node.cc中,
Environment* env = CreateEnvironment( node_isolate, uv_default_loop(), context, argc, argv, exec_argc, exec_argv);
这段代码建立了一个node执行环境,可以看到第三行的uv_default_loop(),这是libuv库中的一个函数,它会初始化uv库本身以及其中的default_loop_struct,并返回一个指向它的指针default_loop_ptr。
之后,Node会载入执行环境并完成一些设置操作,然后启动event loop:
bool more; do { more = uv_run(env->event_loop(), UV_RUN_ONCE); if (more == false) { EmitBeforeExit(env); // Emit `beforeExit` if the loop became alive either after emitting // event, or after running some callbacks. more = uv_loop_alive(env->event_loop()); if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0) more = true; } } while (more == true); code = EmitExit(env); RunAtExit(env); ...
more用来标识是否进行下一轮循环。
env->event_loop()会返回之前保存在env中的default_loop_ptr,uv_run函数将以指定的UV_RUN_ONCE模式启动libuv的event loop。在这种模式下,uv_run会至少处理一个事件:这意味着,如果当前事件队列中没有需要处理的I/O事件,uv_run会阻塞住,直到有I/O事件需要处理,或者下一个定时器时间到。如果当前没有I/O事件也没有定时器事件,则uv_run返回false。
接下来Node会根据more的情况决定下一步操作:
如果more为true,则继续运行下一轮loop。
如果more为false,说明已经没有等待处理的事件了,EmitBeforeExit(env);触发进程的'beforeExit'事件,检查并处理相应的处理函数,完成后直接跳出循环。
最后触发'exit'事件,执行相应的回调函数,Node运行结束,后面会进行一些资源释放操作。
在libuv中,定时器事件是直接在event loop中处理的,而I/O事件则分为两类:
Network I/O是使用系统提供的非阻塞式I/O解决方案,例如在Linux上使用epoll,windows上使用IOCP。
文件操作和DNS操作没有(很好的)系统解决方案,因此libuv自建了线程池,在其中进行阻塞式I/O。
另外我们也可以将自定义的函数抛到线程池中运行,在运行结束后主线程会执行相应的回调函数,不过Node并没有将这一项功能加入到JavaScript中,也就是说只用原生Node是无法在JavaScript中开启新的线程进行并行执行的。
以上所述就是本文的全部内容了,希望大家能够喜欢。

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

Video Face Swap
使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

基于无阻塞、事件驱动建立的Node服务,具有内存消耗低的优点,非常适合处理海量的网络请求。在海量请求的前提下,就需要考虑“内存控制”的相关问题了。 1. V8的垃圾回收机制与内存限制 Js由垃圾回收机

文件模块是对底层文件操作的封装,例如文件读写/打开关闭/删除添加等等 文件模块最大的特点就是所有的方法都提供的**同步**和**异步**两个版本,具有 sync 后缀的方法都是同步方法,没有的都是异

并发编程中的事件驱动机制通过在事件发生时执行回调函数来响应外部事件。在C++中,事件驱动机制可用函数指针实现:函数指针可以注册回调函数,在事件发生时执行。lambda表达式也可以实现事件回调,允许创建匿名函数对象。实战案例使用函数指针实现GUI按钮点击事件,在事件发生时调用回调函数并打印消息。

事件循环是 Node.js 的基本组成部分,通过确保主线程不被阻塞来实现异步编程,了解事件循环对构建高效应用程序至关重要。下面本篇文章就来带大家深入了解Node中的事件循环 ,希望对大家有所帮助!

node无法用npm命令是因为没有正确配置环境变量,其解决办法是:1、打开“系统属性”;2、找到“环境变量”->“系统变量”,然后编辑环境变量;3、找到nodejs所在的文件夹;4、点击“确定”即可。

最开始的时候 JS 只在浏览器端运行,对于 Unicode 编码的字符串容易处理,但是对于二进制和非 Unicode 编码的字符串处理困难。并且二进制是计算机最底层的数据格式,视频/音频/程序/网络包

如何使用 Node.js 进行前端应用的开发?下面本篇文章给大家介绍一下Node开发前端应用的方法,涉及到表现层应用的开发。我今天分享的方案是针对简单场景的,旨在让前端开发人员不必掌握太多关于 Node.js 的背景知识和专业知识,即使没有代码编写经验,也能完成一些简单的服务端开发任务。
