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 相关文章推荐
ASP SQL防注入的方法
Dec 25 Javascript
JavaScript 函数式编程的原理
Oct 16 Javascript
7款风格新颖的jQuery/CSS3菜单导航分享
Apr 23 Javascript
jQuery实现新消息在网页标题闪烁提示
Jun 23 Javascript
javascript实现状态栏中文字动态显示的方法
Oct 20 Javascript
跟我学习javascript的prototype,getPrototypeOf和__proto__
Nov 17 Javascript
在其他地方你学不到的jQuery小贴士和技巧(欢迎收藏)
Jan 20 Javascript
jquery跟随屏幕滚动效果的实现代码
Apr 13 Javascript
Vue.js实现拖放效果的实例
Sep 30 Javascript
使用vue-aplayer插件时出现的问题的解决
Mar 02 Javascript
JS脚本实现定时到网站上签到/签退功能
Apr 22 Javascript
vue中解决微信html5原生ios虚拟键返回不刷新问题
Oct 20 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
DC这些乐高系列动画电影你看过几部?
2020/04/09 欧美动漫
两个开源的Php输出Excel文件类
2010/02/08 PHP
php递归删除目录下的文件但保留的实例分享
2014/05/10 PHP
thinkphp缓存技术详解
2014/12/09 PHP
PHP反射机制原理与用法详解
2017/02/15 PHP
解决windows上php xdebug 无法调试的问题
2020/02/19 PHP
php5.3/5.4/5.5/5.6/7常见新增特性汇总整理
2020/02/27 PHP
javaScript 读取和设置文档元素的样式属性
2009/04/14 Javascript
初学js 新节点的创建 删除 的步骤
2011/07/04 Javascript
js验证整数加保留小数点的简单实例
2013/12/02 Javascript
使用Raygun来自动追踪AngularJS中的异常
2015/06/23 Javascript
Bootstrap CSS组件之输入框组
2016/12/17 Javascript
教你一步步用jQyery实现轮播器
2016/12/18 Javascript
利用js判断手机是否安装某个app的多种方案
2017/02/13 Javascript
JavaScript使用readAsDataURL读取图像文件
2017/05/10 Javascript
Angular模版驱动表单的使用总结
2018/05/05 Javascript
Vue项目打包部署到iis服务器的配置方法
2019/10/14 Javascript
逐行分析鸿蒙系统的 JavaScript 框架(推荐)
2020/09/17 Javascript
[02:03]永远的信仰DOTA2 中国军团历届国际邀请赛回顾
2016/06/26 DOTA
[43:43]完美世界DOTA2联赛PWL S2 FTD.C vs Rebirth 第一场 11.22
2020/11/24 DOTA
[46:47]完美世界DOTA2联赛PWL S2 FTD vs Magma 第二场 11.20
2020/11/23 DOTA
Python字符串的encode与decode研究心得乱码问题解决方法
2009/03/23 Python
Python 使用SMTP发送邮件的代码小结
2016/09/21 Python
Python实现图片滑动式验证识别方法
2017/11/09 Python
python绘制条形图方法代码详解
2017/12/19 Python
Python3.5 处理文本txt,删除不需要的行方法
2018/12/10 Python
Python3.6实现根据电影名称(支持电视剧名称),获取下载链接的方法
2019/08/26 Python
Ranorex通过Python将报告发送到邮箱的方法
2020/01/12 Python
使用Numpy对特征中的异常值进行替换及条件替换方式
2020/06/08 Python
超级搞笑检讨书
2014/01/15 职场文书
会议欢迎标语
2014/06/30 职场文书
十八大标语口号
2014/10/09 职场文书
求职信范文怎么写
2015/03/19 职场文书
中学语文教学反思
2016/02/16 职场文书
2016年小学端午节活动总结
2016/04/01 职场文书
Python内置数据类型中的集合详解
2022/03/18 Python