等待 NodeJS 中的异步方法

Waiting for an Asynchronous method in 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 模块,其中 none 模块不起作用。我什至研究了 yield 方法,但它似乎还没有完全实现到 V8 引擎中。

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

有没有人遇到过这个问题并找到了解决这个问题的设计模式?

您无法在 node.js 中将异步函数转换为同步函数。就是做不到。

如果你有一个异步结果,你不能return它同步或等待它。您将不得不重新设计界面以使用异步界面(这几乎总是涉及传递一个将在结果准备就绪时调用的回调)。

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

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

看来我想做的事情是不可能的,而且这两个模型有冲突。为了最终实现我想要的,我将我的模块封装到一个类似数组的对象中,并带有一些事件方法,这些方法将把它传递给模块的对象,该对象继承了 async-eventemitter class.

所以,这样想...

  • 我的自定义应用程序模块可能会继承 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);
});

然后要执行它,我会做类似 (express)...

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 模型。

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