Philipp Flenker Philipp Flenker - 5 months ago 17
Node.js Question

Implement blocking control flow in NodeJS

I am trying to create a blocking control flow, but have hit a dead end.
The idea is that a request comes in and flows through a number of handler functions. Each function does something and decides whether or not the request is now fulfilled. If it is, the handling stops, if not, the next handler is invoked.
Each handler might or might not do something asynchronously (like fetching/writing data). If that is the case, then the following handlers depend on the data being there before being started.

So far, I have two ideas, which both do not quite match these requirements:
1) All handlers are functions, which are pushed into an array and iterated over with

some
. If a handler wishes to stop the control flow, it simply needs to return
true
.
With this approach, I can't have any of the handlers invoking asynchronous functions.

2) All handlers are promises which are chained. This seems like a better idea to me, but I can't figure out how to best handle stopping the control flow. I have two ideas:
- Keep a variable which is set to
true
once a handler decides to interrupt the flow. Check this value in every single handler and resolve immediately, if needed. This means a lot of duplicate code.
- Rejecting a promise if it does not wish to continue the normal flow, and continuing in
catch
. However, the thought of using errors as a means of controlling the flow pains me - and also it means that I have to handle the case where the catch is invoked because of an actual error.

My spidey senses tell me there has to be another way, but I can't figure it out.

Answer

Don't chain the promise handlers, nest them. You can do that programmatically from an array of functions:

var handlers = […]; // functions that can return promises

var run = handlers.reduceRight(function(next, handler) {
    return function() {
        return Promise.resolve(someArg).then(handler).then(function(result) {
            if (result) // true, whatever
                return result;
            else
                return next();
        });
    };
}, function end() {
    return Promise.reject(new Error("no handler matched")));
});

run().then(function(result) {
    // the result from the first handler that returned anything
}, function(err) {
    // some handler threw an exception, or there was no handler
});