我已经看了高和低,只能找到如何写异步函数,我已经明白了。
我想做的是在一个触发事件[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过载问题。
有没有人经历过这种情况,并找到了一个设计模式来解决这个问题?
你不能在node.js中将异步函数转换为同步函数。这是不可能的。
如果你有一个异步的结果,你不能同步地返回它或者等待它。您将不得不重新设计接口以使用异步接口(这几乎总是涉及传递回调,当结果准备好时将调用该回调)。
如果你想在你.emit()
一个事件之后做一些事情,这个事件本身就是异步的,你想等到异步的事情完成后再做,那么事件发射器可能不是正确的接口。你宁愿有一个函数调用返回一个承诺或回调作为参数。你可以操纵一个eventEmitter来使用它,但是你必须在异步操作完成时发回第二个事件,并且让原始调用者在接收到第二个事件之前不做它的第二部分(这真的不是一个好方法)。
底线-你需要一个不同的设计来处理异步响应(例如回调或承诺)
似乎我想做的是不可能的,两个模型冲突。为了最终实现我想要的,我将模块封装到一个类似数组的对象中,并使用一些事件方法将其传递给模块的对象,该对象继承了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);
});
然后要执行它,我可以这样做(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模型。
(例如,我为我们加载的文件添加了图像数据,我们是图像文件。如果有人想要代码,让我知道,我很乐意分享我所做的)