2

I have some code that fetches data via a fast path, and I want to implement fallback to a slow path if the fast path isn't available. Still very new to node.js here so I'm not sure how to go about this.

Here's a simple example of what I mean:

function getThing(callback) {
    var thang = getThingTheFastWay();
    if (!thang) {
        thang = getThingTheSlowWay();
    }
    if (!thang || !thang.validate()) {
        return callback(new Error('invalid thang'));
    }
    callback(null, thang);
}

So my goal here is to do the I/O in getThingTheSlowWay() asynchronously. Does the second half of this method need to be a callback supplied to getThingTheSlowWay()?

Chris Adams
  • 1,008
  • 1
  • 11
  • 22
  • How is it determined if the fast path is available or not? Since node.js is asynchronous the `thang` may always be undefined depending on how long getThingTheFastWay() takes to execute. – Mike G Aug 21 '15 at 00:39

4 Answers4

1

Does the second half of this method need to be a callback supplied to getThingTheSlowWay()?

Yes, exactly. You could not do the thang = for an asynchronous result. Your function would basically look like this:

function getThing(callback) {
    var thang = getThingTheFastWay();
    if (thang) {
        if (!thang.validate())
            callback(new Error('invalid thang'));
        else
            callback(null, thang);
    } else {
        getThingTheSlowWay(function(err, thang) {
            if (!err && !thang.validate())
                callback(new Error('invalid thang'));
            else
                callback(err, thang);
        });
    }
}

To avoid that duplication, you'd use named function:

function getThing(callback) {
    function validateThang(err, thang) {
        if (!err && !thang.validate())
            callback(new Error('invalid thang'));
        else
            callback(err, thang);
    }
    var thang = getThingTheFastWay();
    if (thang)
        validateThang(null, thang);
    else
        getThingTheSlowWay(validateThang);
}

An alternative would be to use promises. Assuming that getThingTheSlowWay returns a promise while getThingTheFastWay does not, it would look like

function getThing() {
    return Promise.resolve(getThingTheFastWay() || getThingTheSlowWay())
    .then(function validateThang(thang) {
        if (thang.validate())
            return thang;
        else
            throw new Error('invalid thang');
    });
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
1

I'd recommend you always use a callback, regardless of whether something is "fast" or not. Just because you assume it's fast, doesn't mean it always will be - and in the land of high-performance servers, why write something that could be blocking when it's just as easy to not?

function getThing(callback) {
    var thang = getThingTheFastWay(function(err, thang) {
        if (thang && !err) {
            callback(err, thang);
        } else {
            getThingTheSlowWay(function(err, thang) {
                callback(err, thang);
            });
        }
    });
}

This of course would require some rewriting of your downstream functions to support callbacks, but well worth doing. And even if you're using a third party function to do it, it's pretty simple to wrap your own callback around a long-running function.

brandonscript
  • 68,675
  • 32
  • 163
  • 220
0

Consider using promises. https://www.promisejs.org/ A promise allows you to perform some action and when complete perform a callback. If fast action fails then you can recover with the slow action. All of these actions can work with promises.

One thing to consider is that the slow operations may not be that slow consideting the fast operation is typically blocking. It could be problematic when hit with lots of requests.

If you dont want to use promises, check if the fast version works and perform the callback immediately and return. If it hasnt returned you will probably need to pass the callback into the get slow way function since that will have the async call that will have a callback to call the callback parameter

nate
  • 26
  • 2
0

Promises might be useful here:

function getThing() {
    return new Promise(function(resolve, reject) {
        getThingTheFastWay().then(function(result) {
            resolve(result);
        }).catch(function() {
            getThingTheSlowWay().then(function(result) {
                resolve(result);
            }).catch(function() {
                reject();
            });
        });
    });
}

To use this would be:

getThing().then(function(result) {
    // do something with the result
});

You can enable promises with the harmony flag, or using a library like bluebird (recommended)

Petah
  • 45,477
  • 28
  • 157
  • 213
  • 2
    Avoid the [promise constructor antipattern](http://stackoverflow.com/q/23803743/1048572)! – Bergi Aug 25 '15 at 10:47
  • @Bergi related: http://stackoverflow.com/questions/32214322/how-do-you-avoid-the-promise-constructor-antipattern-with-promise-all – Petah Aug 25 '15 at 21:22