Convention to preemptively break out of a domain/action?

38 views
Skip to first unread message

Bradley Meck

unread,
Jan 7, 2013, 10:47:58 AM1/7/13
to nod...@googlegroups.com
Anyone have a sane and performant way that they break out of a domain or action such as middleware? I am trying to design such behavior but am faced with a multitude of interesting issues. The classic example of this problem can be seen with Array.prototype.forEach (though this has the easier synchronous execution for breaking):

```javascript
// we only want to print first 2 items
[1, 2, 3].forEach(function (item) {
  if (item > 2) {
    return;
  }
  console.log(item);
});
```

We often see the error first callback cause a fast fail on waterfall style control flow, but what if we are not issuing an error, but rather a terminating condition (making all middleware after it ignored)? Right now my only solution is to add another callback to arguments ala:

```javascript
function finish(creds, finish, next) {
  if (creds == 'secret') finish(null, 'user/mary');
  else next(null, creds);
}
actor.perform('auth', creds, finish, next);
```

This seems, a bit... overkill to type out though. Anyways, looking for suggestions.

Isaac Schlueter

unread,
Jan 7, 2013, 11:33:03 AM1/7/13
to nodejs
On Mon, Jan 7, 2013 at 7:47 AM, Bradley Meck <bradle...@gmail.com> wrote:
> ```javascript
> // we only want to print first 2 items
> [1, 2, 3].forEach(function (item) {
> if (item > 2) {
> return;
> }
> console.log(item);
> });
> ```

More efficient, especially for long lists:

```javascript
[1, 2, 3].some(function(item, index) {
console.log(item);
return index === 1;
});
```

(I know that's not really your question, sorry.)

Bradley Meck

unread,
Jan 7, 2013, 11:39:36 AM1/7/13
to nod...@googlegroups.com, i...@izs.me
This is good, gets me thinking. This brings up that there are 2 styles of approaching this issue:

abort style - manually cause the action to fast fail
condition style - provide a condition to check on every step of the action

May help others thinking about this as well to think of both perspectives, personally I prefer abort style when available to avoid unnecessary condition checks. But condition checks can be reduced to a boolean, so that may be a suitable solution still.

Marco Rogers

unread,
Jan 8, 2013, 2:32:11 AM1/8/13
to nod...@googlegroups.com, i...@izs.me
Condition style is always available as an option. But in the types of situations you're describing, where you actually want to end the execution early, you end up using condition style because abort style does not exist. That's the case with array.forEach, and it's one of the reasons array.some was added.

But your server middleware example is more complicated I think. Not only are we talking about an asynchronous environment, it's also not clear what the "abort" step is because this is presumably a server middleware chain. Each middleware isn't aware of those around it. It just calls next. You're assuming the middleware knows enough to decide the request can't be fulfilled, in which case it needs to "finish". Whatever that means.

Instead, you could consider that all the middle needs to do is tell you if it succeeded or failed. And your middleware framework could have some kind of branching strategy. Does "finish" complete the request early? Does it skip just the next middleware? Does it start a completely different middleware chain? I don't know, but it may not be a good pattern to start adding ad hoc exceptions into what should be a predictable middleware harness.

Sorry, I may be over thinking your problem here. But I'm interested because this is something I've thought about for geddy as well. I'd be curious to know more about an example situation where you run into this issue.

:Marco

Bradley Meck

unread,
Jan 8, 2013, 3:43:15 AM1/8/13
to nod...@googlegroups.com, i...@izs.me
Completely agree that not having the default action at the end would cause issues in a single middleware stack.

I use middleware trees rather than single stack with Understudy, it would go to the end of an action (for example the default action such as just closing the HTTP request). I run into this problem a fair amount when making pluggable servers. For example:

```javascript
server.before('http.auth', databaseLogin)
server.before('http.auth', googleLogin)
server.before('http.auth', twitterLogin)
server.before('http.auth', anonymousLogin)
```

If one of these passes, we want to skip to the end of the 'http.auth' action. This gets even more complicated when you start using trees (example from an existing code base [sorry for the inlining]):

```javascript
server.perform('http.auth', req, res, function (err, req, res) {
  // default action if middleware do not pick it up
  // people can listen on('end') if they really want to do odd things out of band with the req / res
  function finish(err, req, res) {
    res.writeHead(404); res.end()
  }
  // use apt middleware for state and just let them route if able
  if (req.authorization) {
    server.perform('http.authorized-request', req, res, finish);
  }
  else {
    server.perform('http.unauthorized-request', req, res, finish);
  }
})
```

Notice in the above example I rely on middleware to not call next() if it can handle a behavior to absolute completion. I think this is just a documentation issue personally for actions, but feel free to disagree. Standardizing if a middleware is a leaf of the control flow vs node is something that is too complex to write in code.

Jake Verbaten

unread,
Jan 8, 2013, 4:59:29 AM1/8/13
to nod...@googlegroups.com
Sounds like you want a pull stream to me. You pull from the data until you dont need it anymore and then stop pulling.

Which can be contrasted to a push stream which will continue pushing at you forcing you to do an if check
and just ignore the excess data.

So I think the issue is your trying to pull from a push stream in your API and it's hard. Try making the input
of whatever your doing a pull stream instead.


On Mon, Jan 7, 2013 at 7:47 AM, Bradley Meck <bradle...@gmail.com> wrote:

--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en

Bradley Meck

unread,
Jan 8, 2013, 9:22:44 AM1/8/13
to nod...@googlegroups.com
I am not sure I understand the use of a pull stream in this context. Can you explain in a small example of how to use a stream instead of domains/middleware/etc. in this fashion for control flow?
Reply all
Reply to author
Forward
0 new messages