等待NodeJS中的异步方法

时间:2015-09-24 05:49:21

标签: javascript node.js asynchronous eventemitter

我看起来很高和很低,只能找到如何编写异步函数,我已经理解了。

我想要做的是在触发事件[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;
    });
});

正如您在最后几行中所看到的,事件处理程序将在添加对象的data属性之前完成。

我需要的是可以将异步功能转换为同步功能并在那里获得结果的东西。所以例如......

obj.data = somemodule.asyncFunction();

我查看了wait.for模块,async模块,但这些都不起作用。我甚至研究过yield方法,但它似乎尚未完全实现到V8引擎中。

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

有没有人经历过这种情况并找到了一种设计模式来解决这个问题?

2 个答案:

答案 0 :(得分:2)

您无法在node.js中将异步功能转换为同步功能。它无法完成。

如果您有异步结果,则无法同步返回或等待它。您将不得不重新设计接口以使用异步接口(这几乎总是涉及传入将在结果准备好时调用的回调)。

如果您想要在.emit()事件本身要异步执行某事并且您想等到异步事件完成之后再做一些事情,那么事件发射器可能不是正确的界面。您宁愿进行函数调用,该函数调用返回promise或将回调作为参数。你可以操纵一个eventEmitter来使用它,但你必须在异步操作完成后回发第二个事件,并让原始调用者不要执行它的第二个部分直到它收到第二个事件(这是真的不是一个好方法去。)

底线 - 你需要一个不同的设计来处理异步响应(例如回调或承诺)。

答案 1 :(得分:0)

似乎我想做的事情是不可能的,这两个模型是冲突的。为了达到我想要的目的,我将模块封装成一个类似于数组的对象,其中包含一些事件方法,将它传递给继承async-eventemitter类的模块对象。

所以,想想就好......

  • 我的自定义应用模块可能会继承async-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模型。

(例如,我添加了我们加载的文件的图像数据,我们是图像文件。如果有人想要代码,请告诉我,我非常乐意分享我所做的事情)