Node.js事件驱动


Posted in Javascript onJune 18, 2015

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中开启新的线程进行并行执行的。

以上所述就是本文的全部内容了,希望大家能够喜欢。

Javascript 相关文章推荐
用Jquery选择器计算table中的某一列某一行的合计
Aug 13 Javascript
通过JS来动态的修改url,实现对url的增删查改
Sep 01 Javascript
基于javascript实现右下角浮动广告效果
Jan 08 Javascript
jquery 将当前时间转换成yyyymmdd格式的实现方法
Jun 01 Javascript
带有定位当前位置的百度地图前端web api实例代码
Jun 21 Javascript
详解jQuery中ajax.load()方法
Jan 25 Javascript
jQuery轻松实现无缝轮播效果
Mar 22 jQuery
vue.js 使用axios实现下载功能的示例
Mar 05 Javascript
Nuxt.js实现校验访问浏览器类型的中间件
Aug 24 Javascript
Vue2.x通用编辑组件的封装及应用详解
May 28 Javascript
多个Vue项目部署到服务器的步骤记录
Oct 22 Javascript
ant design charts 获取后端接口数据展示
May 25 Javascript
详解AngularJS的通信机制
Jun 18 #Javascript
javascript背景时钟实现方法
Jun 18 #Javascript
移动Web中图片自适应的两种JavaScript解决方法
Jun 18 #Javascript
javascript随机显示背景图片的方法
Jun 18 #Javascript
利用JavaScript的AngularJS库制作电子名片的方法
Jun 18 #Javascript
javascript实现根据时间段显示问候语的方法
Jun 18 #Javascript
javascript显示中文日期的方法
Jun 18 #Javascript
You might like
COM in PHP (winows only)
2006/10/09 PHP
php启动时候提示PHP startup的解决方法
2013/05/07 PHP
PHP.ini中配置屏蔽错误信息显示和保存错误日志的例子
2014/05/12 PHP
thinkphp如何获取客户端IP
2015/11/03 PHP
PHP排序算法之基数排序(Radix Sort)实例详解
2018/04/21 PHP
可缩放Reloaded-一个针对可缩放元素的复用组件
2007/03/10 Javascript
js 分页全选或反选标识实现代码
2011/08/09 Javascript
jQuery学习笔记之jQuery+CSS3的浏览器兼容性
2015/01/19 Javascript
javascript弹出拖动窗口
2015/08/11 Javascript
jQuery中$.ajax()和$.getJson()同步处理详解
2015/08/12 Javascript
JavaScript 定时器 SetTimeout之定时刷新窗口和关闭窗口(代码超简单)
2016/02/26 Javascript
谈一谈jQuery核心架构设计
2016/03/28 Javascript
JavaScript数组去重的几种方法效率测试
2016/10/23 Javascript
Vue实现按钮旋转和移动位置的实例代码
2018/08/09 Javascript
AjaxFileUpload.js实现异步上传文件功能
2019/04/19 Javascript
vue 集成jTopo 处理方法
2019/08/07 Javascript
vue 限制input只能输入正数的操作
2020/08/05 Javascript
vue开发chrome插件,实现获取界面数据和保存到数据库功能
2020/12/01 Vue.js
[05:31]DOTA2英雄梦之声_第04期_光之守卫
2014/06/23 DOTA
用Python实现一个简单的能够上传下载的HTTP服务器
2015/05/05 Python
用python简单实现mysql数据同步到ElasticSearch的教程
2018/05/30 Python
python pip源配置,pip配置文件存放位置的方法
2019/07/12 Python
Django接收post前端返回的json格式数据代码实现
2019/07/31 Python
Python 在OpenCV里实现仿射变换—坐标变换效果
2019/08/30 Python
解决PyCharm不在run输出运行结果而不是再Console里输出的问题
2020/09/21 Python
恶意软件的定义
2014/11/12 面试题
理工科学生的自我评价
2013/12/15 职场文书
个人简历自我评价
2014/02/02 职场文书
本科毕业生应聘求职信
2014/07/06 职场文书
公务员考察材料
2014/12/23 职场文书
幼儿园教师岗位职责
2015/04/02 职场文书
银行求职信怎么写
2019/06/20 职场文书
2019企业文化管理制度范本!
2019/08/06 职场文书
详解Nginx启动失败的几种错误处理
2021/04/01 Servers
background-position百分比原理详解
2021/05/08 HTML / CSS
关于Nginx中虚拟主机的一些冷门知识小结
2022/03/03 Servers