5

I've looked high and low, and can only find how to write async functions, which I already understand.

What I am trying to do is run an async method in a triggered event [EventEmitter], but such a simple thing seems to be just simply not possible as I can find.

Consider the following...

// 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;
    });
});

As you can see in the last few lines, the event handler will finish before the object's data property is added.

What I need is something where I can turn the async function into an sync function and get the results there and then. so for example...

obj.data = somemodule.asyncFunction();

I've looked at the wait.for module, the async module, and none of these will not work. I've even looked into the yield method, but it seems not yet fully implemented into the V8 engine.

I've also tried using a while loop too wait for data to populate, but this just brings with it the CPU overload issue.

Has anyone experienced this and found a design pattern to get around this?

Guy Park
  • 959
  • 12
  • 25
  • Also, I can't do away with the EventEmitter model, as I use this for modules to attach into crud functions for modularity. – Guy Park Sep 24 '15 at 05:50
  • In CPS you can't escape nested callbacks. With promises you can organize the code a bit more linearly. With generators (yield) you can write synchronous looking code. In any case, when you go async there is no way back, you are basically trapped in async world. – elclanrs Sep 24 '15 at 05:52
  • Event listeners are **not** called synchronously. – Dan D. Sep 24 '15 at 05:53
  • @elclanrs, was afraid of that... what is baffling is that they present EventEmitter being synchronous. I would love to just stick with the callback method, but I really need the event method for anonymous connectivity from other modules (basically a hook system). What about the design pattern for running a callback system, maybe I could pass a next() as my last param in the emit() – Guy Park Sep 24 '15 at 05:59
  • May be interested in http://stackoverflow.com/questions/32010458/how-to-use-asynchronous-method-in-a-synchronous-method/32014644#32014644, or http://stackoverflow.com/questions/24170530/how-to-wait-for-a-promise-to-be-resolved/31107351#31107351. ALso, although "yield" is fully implemented, although it's just part of a solution for writing async functions in a synchronous-like manner. –  Oct 05 '15 at 04:05
  • Thanks @torazaburo, yeah did have a look at the new awesome yield stuff, and tried a few implementations. The only downside was that I needed to enable it via CLI flag as it seems to still be experimental. Long story short, just seemed to problematic. But defiantly hanging out for when it does become common place. – Guy Park Oct 12 '15 at 04:16

2 Answers2

1

You cannot turn an async function into a synchronous one in node.js. It just cannot be done.

If you have an asynchronous result, you cannot return it synchronously or wait for it. You will have to redesign the interface to use an asynchronous interface (which nearly always involves passing in a callback that will be called when the result is ready).

If you're wanting to do something after you .emit() an event that is itself going to do something asynchronously and you want to wait until after the async thing finished, then the event emitter is probably not the right interface. You'd rather have a function call that returns a promise or takes a callback as an argument. You could manipulate an eventEmitter to use this, but you'd have to post back a second event when the async operation finished and have the original caller not do it's second part until it receives the second event (which is really not a good way to go).

Bottom line - you need a different design that works with async responses (e.g. callbacks or promises).

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • so basically the EventEmitter is a design pattern that just does not work with NodeJS's basic design...... I am trying to stay away from dependancy linking, as I would like to pick and choose modules to add to my system based on the requirement of the setup and was thinking global events would do the trick. Do you have any design patters to replace the EventEmitter object that would allow for easy plug and play of modules. I've seen 'async-eventemitter' which looks promising, but would be good to replace ExpressJS's event system. – Guy Park Sep 24 '15 at 06:24
  • 1
    @GuyPark - no I wouldn't characterize it that way. The eventEmitter is for notifying of an event. It is not meant as a substitute for a function call which is how you are trying to use it. You're trying to `emit()` an event that causes a side effect asynchronously and then after emitting the event, you're trying to then use that side effect. That's just not how the event emitter was ever intended. Use a function call with a callback or a promise for what you're doing. Those are the structures that were designed for this. Async development requires using the proper tools. – jfriend00 Sep 24 '15 at 06:26
  • @GuyPark - Did this answer solve your issue? If so, please mark an accepted answer by checking the green checkmark to the left of that answer to indicate to the community that your question has been answered and then both you and the person who provided the answer will earn some reputation points that can lead to more privileges here on StackOverflow. – jfriend00 Sep 27 '15 at 17:27
  • Hey @jfriend00, not really. I was hoping for a design pattern to help, but in the end I created an object that encapsulated my modules, that inherited the 'async-eventemitter'. So now I can sort-of emit (async chaining) an event, or at least pass it on for something else to handle. I'll update my question to include the example. Thanks for your help though. – Guy Park Oct 05 '15 at 02:30
0

Seems what I wanted to do is just not possible and the two models conflict. To achieve what I wanted in the end, I encapsulated my modules into an array-like object with some event methods that will pass it on to the module's object, which inherited the async-eventemitter class.

So, think of it like so...

  • My custom app modules may inherit the async-eventemitter module so they have the .on() and .emit(), etc. methods.
  • I create a customised array item, that will allow me to pass an event on to the module in question that will work asynchronously.

The code I created (and this is by no means complete or perfect)...

// 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);
        }
    }
} });

This then allows me to bind an event handler to a module by simply calling something like the following... .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);
});

And then to execute it I would do something like (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();
   });
});

Again, this is just an example of what I did, so i've probably missed something in my copy above, but effectively it's the design I ended up using and it worked like a treat, and I was able to extend data on an object before being pushed out to my HTTP response, without having to replace the main [expressjs] object's standard EventEmitter model.

(eg, I added image data for files that we're loaded that we're image files. If anyone wants the code, let me know, I am more than happy to share what I did)

Guy Park
  • 959
  • 12
  • 25