2015-09-24 313 views
2

我看起来很高,并且只能找到如何编写异步函数,这我已经理解了。等待NodeJS中的异步方法

我想要做的是在触发事件[EventEmitter]中运行异步方法,但是这样一个简单的事情似乎只是根本不可能,因为我可以找到。

考虑以下...

// Your basic async method.. 
function doSomething(callback) { 
    var obj = { title: 'hello' }; 

    // Fire an event for event handlers to alter the object. 
    // EvenEmitters are called synchronously 
    eventobj.emit('alter_object', obj); 

    callback(null, obj); 
} 

// when this event is fired, I want to manipulate the data 
eventobj.on('alter_object', function(obj) { 
    obj.title += " world!"; 

    // Calling this async function here means that our 
    // event handler will return before our data is retrieved. 
    somemodule.asyncFunction(callback(err, data) { 
     obj.data = data; 
    }); 
}); 

正如你可以在最后几行看到,添加对象的数据属性之前的事件处理程序将结束。

我需要的是可以将异步函数转换为同步函数并在那里获得结果的东西。因此,例如...

obj.data = somemodule.asyncFunction(); 

我已经看过了wait.for模块,该模块async,而这些都不是行不通的。我甚至研究过yield方法,但它似乎还没有完全实现到V8引擎中。

我也试过使用while循环太等待数据填充,但这只是带来了CPU超载的问题。

有没有人遇到过这种情况,并找到了解决此问题的设计模式?

+0

还,我不能废除EventEmitter模型,因为我使用这个模块来附加模块化的crud函数。 –

+0

在CPS中,你不能逃避嵌套的回调。有了承诺,您可以更线性地组织代码。使用生成器(yield),您可以编写同步代码。在任何情况下,当你走异步都没有回头路时,你基本上陷入了异步世界。 – elclanrs

+0

事件监听器是**不**同步调用。 –

回答

0

似乎我想做的事情是不可能的,两种模式相冲突。为了最终达到我想要的效果,我将模块封装到一个类似数组的对象中,并使用一些事件方法将其传递给模块的对象,该对象继承了async-eventemitter类。

所以,认为它是这样的...

  • 我的自定义应用程序模块可以继承异步eventemitter模块,以便他们有.on().emit()等方法。
  • 我创建了一个自定义的数组项,这将允许我将事件传递给将会异步工作的模块。

我创建的代码(这是不完整或完善)...

// My private indexer for accessing the modules array (below) by name. 
var module_dict = {}; 

// An array of my modules on my (express) app object 
app.modules = []; 

// Here I extended the array with easier ways to add and find modules. 
// Haven't removed some code to trim down this. Let me know if you want the code. 
Object.defineProperty(app.modules, 'contains', { enumerable: false, ... }); 
Object.defineProperty(app.modules, 'find', { enumerable: false, ... }); 

// Allows us to add a hook/(async)event to a module, if it exists 
Object.defineProperty(app.modules, 'on', { enumerable: false, configurable: false, value: function(modulename, action, func) { 
    if (app.modules.contains(modulename)) { 
     var modu = app.modules.find(modulename); 
     if (modu.module && modu.module['on']) { 
      // This will pass on the event to the module's object that 
      // will have the async-eventemitter inherited 
      modu.module.on(action, func); 
     } 
    } 
} }); 
Object.defineProperty(app.modules, 'once', { enumerable: false, configurable: false, value: function(modulename, action, func) { 
    if (app.modules.contains(modulename)) { 
     var modu = app.modules.find(modulename); 
     if (modu.on) { 
      modu.on(action, func); 
     } 
    } 
} }); 

这就让我通过简单地调用类似如下的事件处理程序模块绑定... .on(module_name, event_name, callback)

app.modules.on('my_special_module_name', 'loaded', function(err, data, next) { 
    // ...async stuff, that then calls next to continue to the next event... 
    if (data.filename.endsWith('.jpg')) 
     data.dimensions = { width: 100, height: 100 }; 

    next(err, data); 
}); 

然后执行它,我会做这样的事情(快递)...

app.get('/foo', function(req, res, next) { 
    var data = { 
     filename: 'bar.jpg' 
    }; 

    // Now have event handlers alter/update our data 
    // (eg, extend an object about a file with image data if that file is an image file). 
    my_special_module.emit('loaded', data, function(err, data) { 
     if (err) next(err); 
     res.send(data); 

     next(); 
    }); 
}); 

再一次,这只是我做过的一个例子,所以我可能错过了上面我的副本中的某些内容,但实际上这是我最终使用的设计,它的工作方式像一种享受,我能够扩展数据在被推送到我的HTTP响应之前,不需要替换主[expressjs]对象的标准EventEmitter模型。

(例如,我添加图像数据,我们正在加载,我们是图像文件的文件。如果有人想代码,让我知道,我很乐意分享我做了什么)

1

您不能在node.js中将异步函数转换为同步函数。这是不能做到的。

如果您有异步结果,则无法同步返回或等待它。您将不得不重新设计接口以使用异步接口(几乎总是需要传递一个将在结果准备就绪时调用的回调函数)。

如果你想在你之后做一些事情.emit()本身将要异步执行某些事情的事件,并且你希望等待异步事件完成之后事件发射器可能不是正确的接口。你宁愿有一个函数调用返回一个承诺或将回调作为参数。你可以操纵一个eventEmitter来使用它,但是当异步操作完成时你必须回发第二个事件,并且让原始调用者在接收到第二个事件之前不会做第二个事件(这实际上不是一个好方法走)。底线 - 您需要与异步响应(例如回调或承诺)配合使用的不同设计。

+0

所以基本上EventEmitter是一个设计模式,只是不能与NodeJS的基本设计一起工作......我试图远离依赖关系链接,因为我想选择模块添加到我的系统基于设置的要求,并认为全球性的事件会做到这一点。您是否有任何设计模式来替换EventEmitter对象,以便轻松实现模块的即插即用。我已经看到了看起来很有希望的'异步均衡器',但是取代ExpressJS的事件系统会很好。 –

+1

@GuyPark - 不,我不会这样描述它。 eventEmitter用于通知事件。它并不意味着你正在尝试使用它的功能调用的替代品。你试图'发射'一个异步引发副作用的事件,然后在发射事件之后,你试图使用副作用。这不仅仅是事件发射器的意图。使用带有回调或承诺的函数调用来处理您正在做的事情。那些是为此设计的结构。异步开发需要使用适当的工具。 – jfriend00

+0

@GuyPark - 这个答案解决了你的问题吗?如果是这样,请通过选中该答案左侧的绿色复选标记来标记已接受的答案,以向社区表明您的问题已得到解答,然后您和提供答案的人都将获得一些可导致更多特权在这里StackOverflow。 – jfriend00