我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Node.js中的事件驱动编程详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Node.js中的事件驱动编程详解

在传统程编程模里,I/O操作就像一个普通的本地函数调用:在函数执行完之前程序被堵塞,无法继续运行。堵塞I/O起源于早先的时间片模型,这种模型下每个进程就像一个独立的人,目的是将每个人区分开,而且每个人在同一时刻通常只能做一件事,必须等待前面的事做完才能决定下一件事做什么。但是这种在计算机网络和Internet上被广泛使用的“一个用户,一个进程”的模型伸缩性很差。管理多个进程时,会耗费很多内存,上下文切换也会占用大量资源,这些对操作系统是个很大的负担,而且随着进程数的递增,会导致系统性能急剧衰减。

多线程是个替代方案,线程是一个轻量级的进程,它会和同一个进程内的其它线程共享内存,它更像传统模型的扩展,用来并发执行多个线程,当一个线程等待I/O操作时,其它线程可以接管CPU,当I/O操作完成,前面等待的线程会被唤醒。就是说,一个运行中的线程可以被中断,然后稍候再被恢复。此外,在一些系统下线程可以在多核CPU的不同核心下并行运行。

程序员并不知道线程会在什么具体时间运行,他们必须很小心的处理共享内存的并发访问,因此必须使用一些同步原语来同步访问某个数据结构,比如使用锁或信号量,以此来强制线程以特定的行为和计划执行。那些大量依赖线程间的共享状态的应用程序,很容易就会出现一些随机性很强,难以查找的奇怪问题。

还有一种方式是使用多线程协作,由你自己负责显式的释放CPU,并把CPU时间交给其他线程使用,因为由你亲自来控制线程的执行计划,因此减小了对同步的需求,但是也提高了程序的复杂度和出错的机会,而且并没有避免多线程的那些问题。

什么是事件驱动编程

事件驱动编程(Evnet-driven programming)是一种编程风格,由事件来决定程序的执行流程,事件由事件处理器(event handler)或事件回调(event callback)来处理,事件回调是当某个特定事件发生时被调用的函数,比如数据库返回了查询结果或者用户单击了一个按钮。

回想下,在传统的堵塞I/O编程模式里,数据库查询可能像这样:

result = query('SELECT * FROM posts WHERE id = 1');

do_something_with(result);


上面的query函数会让当前线程或进程一直处于等待状态,直到底层数据库完成查询操作并返回。

在事件驱动模型里,这个查询会变成这样:

query_finished = function(result) {

do_something_with(result);

}

query('SELECT * FROM posts WHERE id = 1', query_finished);

首先你定义了一个叫query_finished的函数,它包含了查询完成后要做的事。然后把这个函数当做参数传递给query函数,当query执行完毕会调用query_finished,而不是仅仅返回查询结果。

当你感兴趣的事件发生时会调用你定义的函数,而不是简单的返回结果值,这种编程模型就叫事件驱动编程或异步编程。这是Node一个最明显的特性,这种编程模型意味着当前进程在执行I/O操作时不会被阻塞,因此,多个I/O操作可以并行执行,当操作完成后相应的回调函数就会被调用。

事件驱动编程底层依赖于事件循环(event loop),事件循环基本上是事件检测和事件处理器触发这两种函数不断循环调用的一个结构。在每次循环里,事件循环机制需要检测发生了哪些事件,当事件发生时,它找到对应的回调函数并调用它。

事件循环只是运行在进程内的一个线程,当事件发生时,事件处理器可以单独运行并且不会被中断,也就是说:

1.在某个特定时刻最多有一个事件回调函数运行
2.任何事件处理器运行时都不会被中断

有了这个,开发人员就可以不再为线程同步和并发修改共享内存这些事头疼了。

一个众所周知的秘密:

很久以前,系统编程社区的人们就知道事件驱动编程是创建高并发服务最佳方式,因为它不用保存很多上下文,因此节省了大量内存,也没有那么多上下文切换,又节省了大量执行时间。

慢慢的,这种理念渗透到了其他的平台和社区,出现了一些有名的事件循环实现,比如Ruby的Event machine,Perl的AnyEvnet,以及Python的Twisted,除了这些还有很多其它的实现和语言。

用这些框架做开发,需要学习框架相关的特定知识以及框架特定的类库,比如,使用Event Machine时,为了享受非阻塞带来的好处,你得避免使用同步类库,只能用Event Machine的异步类库。如果你使用了任何阻塞类库(比如Ruby的大多数标准库),你的服务器就失去了最佳的伸缩性,因为事件循环依然会不断地被阻塞,时不时地阻碍了I/O事件的处理。

Node最初就被设计成一个非阻塞I/O服务器平台,因此一般情况下,你应该期望运行在它上面的所有代码都是非阻塞的。因为JavaScript非常小,而且它不强制使用任何I/O模型(因为它没有标准的I/O类库),因此Node建立在一个很纯净的环境里,不会有什么历史遗留问题。

Node和JavaScript如何简化了异步应用程序

Node的作者Ryan Dahl,最初使用C来开发这个项目,但是发现维护函数调用的上下文太复杂,导致代码复杂度很高。然后他转用Lua,但是Lua已经有个几个阻塞的I/O类库,阻塞和非阻塞混在一起可能会让开发人员很迷惑并因此阻碍了很多人构建可伸缩的应用,于是Lua也被Dahl抛弃了。最后他转向了JavaScript,JavaScript中的闭包及第一级对象的函数,这些特性使JavaScript非常适合用作事件驱动编程。JavaScript的魔力是让Node如此流行的一个主要原因。

什么是闭包

闭包可以理解为一个特殊的函数,但是它可以继承并访问它自身被定义的那个作用域里的变量。当你将一个回调函数作为参数传递给另外一个函数时,它稍候会被调用,神奇的是,这个回调函数被稍候调用时,它居然记住了它自身定义所在的那个上下文以及父上下文里的变量,而且还可以正常访问它们。这个强大的特性是Node成功的核心。

下面的例子将展示在Web浏览器里JavaScript闭包是如何工作的。假如,你要监听一个按钮的单机事件,你可以这样做:

var clickCount = 0;

document.getElementById('myButton').onclick = function() {

clickCount += 1;

alert("clicked " + clickCount + " times.");

};

使用jQuery时是这样:

var clickCount = 0;

$('button#mybutton').click(function() {

clickedCount ++;

alert('Clicked ' + clickCount + ' times.');

});

JavaScript里,函数是第一类对象,就是说你可以把函数当作参数来传递给其他函数。上面的两个例子,前者把一个函数赋值给另一个函数,后者把函数作为参数传递给另一个函数,单击事件的处理函数(回调函数)可以访问函数定义所在代码块下的每个变量,在这个例子里,它可以访问在它父闭包内定义的clickCount变量。

clickCount变量处在全局作用域(JavaScript里最外层的作用域),它保存了用户点击按钮的次数,通常在全局作用域下存储变量是个坏习惯,因为那样很容易跟其他代码冲突,你应该把变量放在使用它们的本地作用域里。大多时候,只用把代码用一个函数包装起来,等于另外创建了闭包,这样就可以很容易避免污染全局环境,就像这样:

(function() {

var clickCount = 0;

$('button#mybutton').click(function() {

clickCount ++;

alert('Clicked ' + clickCount + ' times.');

});

}());


注意:上面代码的第七行,定义了一个函数后立刻调用它,这是JavaScript里一个常见的设计模式:通过创建函数来创建一个新的作用域。

闭包如何帮助异步编程

在事件驱动编程模型里,先编写事件发生后将要运行的代码,然后把这些代码放到一个函数里,最后把这个函数当作参数传递给调用者,稍后由调用者函数调用。

在JavaScript里,一个函数并不是个孤立的定义,它同时会记住自己被声明的那个作用域的上下文,这种机制让JavaScript的函数可以访问函数定义所在那个上下文及父上下文里的所有变量。

当你把一个回调函数当作参数传递给调用者后,这个函数就会在稍后的某个时刻被调用。即使定义回调函数的那个作用域已经结束,在回调函数被调用时,它依然能够访问这个已结束的作用域及其父作用域里的所有变量。像最后那个例子,回调函数在jQuery的click()内部被调用,它却依然能访问clickCount变量。

前面展现了闭包的神奇之处,把状态变量传递给一个函数就可以让你不用维护状态就能进行事件驱动编程,JavaScript的闭包机制会帮你维护它们。

小结

事件驱动编程是一种通过事件触发来决定程序执行流程的编程模型。程序员为他们感兴趣的事件注册回调函数(通常被称作事件处理器),然后系统在事件发生时调用已注册的事件处理器。这种编程模型有很多传统阻塞编程模型所不具备的优势,以前要实现类似的特性,就必须使用多进程/多线程才行。

JavaScript是种强大的语言,因为它的第一类型对象的函数和闭包特性,让它很适合事件驱动编程。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Node.js中的事件驱动编程详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Node.js中的事件驱动编程详解

在传统程编程模里,I/O操作就像一个普通的本地函数调用:在函数执行完之前程序被堵塞,无法继续运行。堵塞I/O起源于早先的时间片模型,这种模型下每个进程就像一个独立的人,目的是将每个人区分开,而且每个人在同一时刻通常只能做一件事,必须等待前面
2022-06-04

探索 Node.js EventEmitter:掌握事件驱动的编程范例

Node.js EventEmitter 是一个强大的事件驱动的编程范例,它允许开发者轻松处理应用程序中的事件。了解 EventEmitter 的机制和用法对于掌握 Node.js 的异步编程至关重要。
探索 Node.js EventEmitter:掌握事件驱动的编程范例
2024-02-19

Node.js 事件发射器的妙用:释放事件驱动编程的潜力

Node.js 事件发射器是一种强大的工具,可以创建响应且高效的事件驱动应用程序。本文探讨了事件发射器的妙用及其带来的优势,并提供了实用代码示例来展示其用法。
Node.js 事件发射器的妙用:释放事件驱动编程的潜力
2024-03-05

Node.js事件循环的奥秘:从单线程到事件驱动编程的蝶变

: Node.js事件循环是Node.js实现并发编程的核心机制。本文将深入剖析Node.js事件循环的奥秘,从单线程到事件驱动编程的蝶变,帮助您全面理解Node.js的运行机制。
Node.js事件循环的奥秘:从单线程到事件驱动编程的蝶变
2024-02-03

理解 Node.js 事件驱动机制的原理

学习 Node.js 一定要理解的内容之一,文中主要涉及到了 EventEmitter 的使用和一些异步情况的处理,比较偏基础,值得一读。大多数 Node.js 对象都依赖了 EventEmitter 模块来监听和响应事件,比如我们常用的
2022-06-04

Django signals:掌握事件驱动的编程

Django signals 可以让你在模型事件发生时触发自定义代码,从而实现更复杂的应用程序行为。Signals 提供了一种灵活的方式来扩展 Django 的功能,并允许你在不修改核心代码的情况下添加自己的业务逻辑。
Django signals:掌握事件驱动的编程
2024-02-16

Node.js 事件驱动编程的陷阱:巧妙规避,保障程序稳定

Node.js 的事件驱动架构带来高并发和响应迅速的特性,但如果处理不当,也会产生隐藏的陷阱,影响程序稳定性。本文深入探讨了 Node.js 事件驱动编程中常见的陷阱,并提供巧妙的规避策略,确保程序稳健运行。
Node.js 事件驱动编程的陷阱:巧妙规避,保障程序稳定
2024-03-05

Linux协程与事件驱动编程的结合

Linux协程和事件驱动编程可以结合在一起,以实现高效的并发编程方式。在Linux系统中,协程可以通过协程库(如libcoroutine)实现,而事件驱动则可以通过事件循环(如libevent)或异步IO库(如libuv)实现。通过将Li
Linux协程与事件驱动编程的结合
2024-08-08

Node.js 事件驱动编程的未来:探索创新与发展方向

Node.js的事件驱动架构为开发人员提供了无与伦比的灵活性和可扩展性。本文探讨了该模型的未来发展方向,重点关注机器学习、物联网和分布式系统方面的创新。
Node.js 事件驱动编程的未来:探索创新与发展方向
2024-03-05

Node.js 事件驱动编程与可伸缩性:探讨异步编程的伸缩策略

Node.js 采用事件驱动编程,支持高并发和可伸缩性。本文探讨异步编程的伸缩策略,帮助开发人员构建更具可扩展性的 Node.js 应用程序。
Node.js 事件驱动编程与可伸缩性:探讨异步编程的伸缩策略
2024-03-05

详解Node.js中的事件机制

前言 在前端编程中,事件的应用十分广泛,DOM上的各种事件。在Ajax大规模应用之后,异步请求更得到广泛的认同,而Ajax亦是基于事件机制的。 通常js给我们的第一印象就是运行在客户端浏览器上面的脚本,通过node.js我们可以在服务端运行
2022-06-04

Node.js 事件驱动编程与并发编程:携手并进,构建高并发系统

Node.js 事件驱动编程和并发编程是构建高并发系统的基础,它们携手合作,充分利用 CPU 资源,处理大量并发请求。本文将深入探讨这两种编程范式,并通过示例代码说明它们如何共同作用。
Node.js 事件驱动编程与并发编程:携手并进,构建高并发系统
2024-03-05

C++ 函数在并发编程中的事件驱动机制?

并发编程中的事件驱动机制通过在事件发生时执行回调函数来响应外部事件。在 c++++ 中,事件驱动机制可用函数指针实现:函数指针可以注册回调函数,在事件发生时执行。lambda 表达式也可以实现事件回调,允许创建匿名函数对象。实战案例使用函数
C++ 函数在并发编程中的事件驱动机制?
2024-04-26

Node.js 事件驱动编程与社区贡献:参与开源,共建生态

Node.js 中的事件驱动编程为开发人员提供了一种高效、响应迅速的开发模型。通过参与开源项目并回馈社区,开发人员可以提高他们的技能、扩大他们的网络并为更广泛的软件生态系统做出贡献。
Node.js 事件驱动编程与社区贡献:参与开源,共建生态
2024-03-05

Node.js 事件驱动编程与错误处理:从容应对异步异常

Node.js 的事件驱动编程模式为异步操作提供了极大的灵活性和可扩展性,但它也带来了错误处理的独特挑战。本文将探讨事件驱动编程中常见的错误类型以及 Node.js 中高效处理它们的最佳实践。
Node.js 事件驱动编程与错误处理:从容应对异步异常
2024-03-05

编程热搜

目录