【问题标题】:Waiting for an Asynchronous method in NodeJS在 NodeJS 中等待异步方法
【发布时间】:2015-12-21 15:15:30
【问题描述】:

我高高在上,只能找到如何写异步函数,我已经明白了。

我正在尝试做的是在触发事件 [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 过载问题。

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

【问题讨论】:

  • 另外,我不能取消 EventEmitter 模型,因为我将它用于模块以附加到 crud 函数中以实现模块化。
  • 在 CPS 中,您无法逃避嵌套回调。使用 Promise,您可以更线性地组织代码。使用生成器(yield),您可以编写同步的代码。无论如何,当你去异步时,没有回头路,你基本上被困在异步世界中。
  • 事件监听器被同步调用。
  • @elclanrs,害怕那个......令人困惑的是他们呈现的 EventEmitter 是同步的。我很想坚持使用回调方法,但我真的需要事件方法来匿名连接来自其他模块(基本上是一个钩子系统)。运行回调系统的设计模式怎么样,也许我可以通过 next() 作为我在 emit() 中的最后一个参数
  • 可能对stackoverflow.com/questions/32010458/…stackoverflow.com/questions/24170530/… 感兴趣。此外,虽然“yield”已完全实现,但它只是以类似同步的方式编写异步函数的解决方案的一部分。

标签: javascript node.js asynchronous eventemitter


【解决方案1】:

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

如果你有一个异步结果,你不能同步返回或等待它。您必须重新设计接口以使用异步接口(这几乎总是涉及传入一个回调,该回调将在结果准备好时调用)。

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

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

【讨论】:

  • 所以基本上 EventEmitter 是一种设计模式,它不适用于 NodeJS 的基本设计......我试图远离依赖链接,因为我想挑选模块根据设置的要求添加到我的系统中,并认为全局事件可以解决问题。您是否有任何设计模式来替换 EventEmitter 对象,以便轻松即插即用模块。我见过“async-eventemitter”,它看起来很有希望,但可以很好地替换 ExpressJS 的事件系统。
  • @GuyPark - 不,我不会那样描述它。 eventEmitter 用于通知事件。它并不意味着可以替代您尝试使用它的函数调用。您正在尝试emit() 一个异步导致副作用的事件,然后在发出事件后,您尝试使用该副作用。这不是事件发射器的初衷。对你正在做的事情使用带有回调或承诺的函数调用。这些是为此设计的结构。异步开发需要使用适当的工具。
  • @GuyPark - 这个答案解决了你的问题吗?如果是这样,请通过选中该答案左侧的绿色复选标记来标记已接受的答案,以向社区表明您的问题已得到解答,然后您和提供答案的人都将获得一些声誉积分,这可能会导致StackOverflow 上的更多权限。
  • 嘿@jfriend00,不是真的。我希望设计模式能有所帮助,但最后我创建了一个封装我的模块的对象,它继承了“async-eventemitter”。所以现在我可以发出(异步链接)一个事件,或者至少将它传递给其他东西来处理。我将更新我的问题以包含该示例。不过感谢您的帮助。
【解决方案2】:

似乎我想做的事情是不可能的,并且两个模型发生冲突。为了最终实现我想要的,我将我的模块封装到一个类似数组的对象中,并使用一些事件方法将其传递给模块的对象,该对象继承了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 模型。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-31
    • 1970-01-01
    • 2019-07-20
    • 1970-01-01
    • 2020-01-31
    • 2021-04-16
    • 1970-01-01
    相关资源
    最近更新 更多