在 NodeJS 中等待异步方法

Guy*_*ark 5 javascript asynchronous node.js 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;
    });
});
Run Code Online (Sandbox Code Playgroud)

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

我需要的是可以将异步函数转换为同步函数并立即获取结果的东西。所以例如...

obj.data = somemodule.asyncFunction();
Run Code Online (Sandbox Code Playgroud)

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

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

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

Guy*_*ark 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);
        }
    }
} });
Run Code Online (Sandbox Code Playgroud)

然后,这允许我通过简单地调用类似以下内容的方式将事件处理程序绑定到模块... .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);
});
Run Code Online (Sandbox Code Playgroud)

然后为了执行它,我会做类似的事情(表达)......

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();
   });
});
Run Code Online (Sandbox Code Playgroud)

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

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