views:

93

answers:

1

I'm pretty new to event-based programming (using node.js). I believe that there's something I'm just not grokking about it, because there's a particular problem that I keep coming across again and again and again.

In short, that problem is dealing with asynchronicity when it seems to be getting in your way. This most often manifests itself, in my case, when working with third-party libraries, which are non-blocking by design and promote a callback-based API.

For instance: Right now I'm writing some stuff that makes heavy use of mranney's node-redis library. My program is scraping RSS feeds and tucking the results into redis. I'm using what I believe is a common strategy with redis:

  1. Scrape feed, store results as a redis hash with a key of something like feed:<feedId>:results:<timestamp>.
  2. Store reference to the latest result under feed:<feedId>:latest.
var redis = require("redis");
var client = redis.createClient();

var get_latest_results = function (feedId) {
    client.get('feed:+ feedId + ':latest', function (err, res) {
        var latest_reading_key = res.toString();
        client.hgetall(latest_reading_key, function (err, res) {
            var latest_reading = res;
        });
    });
    // how do I specify a return value for this function?
}

Placing return latest_reading at the bottom of the get_latest_results function fails, because latest_reading isn't defined until after the function is ready to exit. Placing return latest_reading within the hgetall call fails because the return refers to the callback, and is ignored by get_latest_results.

This is just one example of the kind of situation I seem constantly to write my way into. Maybe I'm trying to pound the square peg into the round hole because I don't know any better. It does seem that there should be a non-hackish way of solving this class of problems.

+10  A: 

You are struggling with asynchrony because you are still writing your functions in a synchronous paradigm.

In asynchrony you should attach callbacks to events. You shouldn't expect a result from an asynchronous function like get_latest_results(), but you should pass it a callback function as an argument to be invoked when the results are ready. The callback will do whatever needs to be done with your results:

var get_latest_results = function (feedId, readyCallback) {
    client.get('feed:' + feedId + ':latest', function (err, res) {
        var latest_reading_key = res.toString();
        client.hgetall(latest_reading_key, function (err, res) {
            readyCallback(res);                           //--- Trigger Callback
        });
    });
    // how do I specify a return value for this function? //--- You don't
}

Then you can call your function like this:

get_latest_results(1000, function (result) {
   //--- Do whatever needs to be done with the latest result...
});
Daniel Vassallo
+1. Learn to like it. 'cos JavaScript doesn't give you the language-level primitives like threads or coroutines you would need to be able to run asynchronous code synchronously (or vice versa).
bobince