对node.js的新手来说,使用回调比事件有什么好处?


24

我是JavaScript新手,对V8引擎内部发生的事情一无所知。

话虽这么说,我真的很喜欢在node.js环境中的早期尝试,但是我发现自己一直在使用events.EventEmitter()作为发出全局事件的一种方式,以便我可以构造程序以适应通知者-观察者模式类似于我将要写的Objective-C或Python程序。

我发现自己一直在做这样的事情:

var events = require('events');

var eventCenter = new events.EventEmitter();

eventCenter.on('init', function() {
    var greeting = 'Hello World!';
    console.log('We're in the init function!);
    eventCenter.emit('secondFunction', greeting);
});

eventCenter.on('secondFunction', function(greeting) {
        console.log('We're in the second function!);
        console.log(greeting);
    eventCenter.emit('nextFunction');
});

eventCenter.on('nextFunction', function {
    /* do stuff */
});

eventCenter.emit('init');

因此,实际上,我只是将“异步” node.js代码结构化为按预期顺序执行操作的代码,如果可以的话,我有点“向后编码”。在性能上或哲学上以大量回调的方式执行此操作会有所区别吗?使用回调而不是事件来做相同的事情更好吗?


如果发现由于大量使用回调而使代码的可读性受到影响,请考虑使用框架来包装回调的使用。它需要一些调整,但是promise常常可以解开丑陋的回调。如果使用的是JQuery,则可以使用DeferredRSVP是最简单的Promise框架之一。
布莱恩

我很困惑,您的代码中看不到任何异步的东西,只是以过于复杂的方式编写的函数调用。
svick

Answers:


27

关于回调的好处是那里没有全局状态,将参数传递给它们很简单。如果您有一个函数,download(URL, callback: (FileData)->void)那么您可以知道这是一个自包含的高阶函数,从本质上讲您可以构造一个“抓住并执行该函数”的函数。您可以确定您的代码流是否完全符合您的期望,因为没有其他人对该回调有任何处理,并且该回调除了父函数的给定参数外,一无所知。这使其模块化并且易于测试。

如果现在要并行下载5个文件并执行其他操作,则只需使用适当的回调函数关闭其中五个函数即可。在具有良好匿名函数语法的语言中,此功能非常强大。

另一方面,事件是为通知1..*用户某些状态更改而设计的。如果您在的末尾触发“下载完成”事件,该事件download(URL)将启动,并且processDownload()该事件知道在何处可以找到数据,那么您会将事情的实现与大量状态联系在一起。您现在如何并行化下载?您如何以不同方式处理不同的下载?是download(URL, eventId)优雅?是downloadAndDoThing(URL)优雅?几乎不。

当然,与所有事物一样,您正在权衡取舍。嵌套的回调会使代码的执行顺序更加混乱,并且缺乏容易的全局访问性使得对于在生产者和消费者之间确实存在1..*关系的任何事情,它都是一个糟糕的选择。您很难传递数据,但是如果您可以不用其他状态而摆脱困境,那通常还是有好处的。

而不管。您在node.js中编码,其中回调是惯用的,并且语言和库是围绕它们的使用而设计的。无论您是在一种设计中还是在另一种设计中都看到了优势,我认为用任何一种语言编写惯用代码几乎都会成真,这将比试图绕过它提供更大的支持,并使您的生活变得更加轻松。


2

我从未在NodeJS中使用过事件,但是通常我使用客户端JS事件的目的是表示发生了某些事情,例如AppointmentBookedEvent,以便SPA视图的不同部分可以对此做出反应(在时间轴上显示,加载它在面板等中)。
但是,使用事件来表示方法已完成可能是一条危险的旅行之路。您不能依赖事件以被触发的相同顺序到达,因此可能导致各种随机性……
使用事件没有什么错,但是有一定程度的粒度是您不应该考虑的; 如果您坚持使用域级的事件,那很好。但是,通知方法已完成太细粒度。


0

您可能会探索的是事件外部对象的使用,这些事件正在发出它们。在您的示例中,eventCenter似乎同时发出和处理自己的事件。考虑如果其他对象开始处理事件,则应用程序的结构可能如何更改。

因此,eventCenter可能会发出“ init”,然后其他对象可以处理它以执行其启动代码,等等。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.