Matt,

Your remark is spot on:

If it didn't have that "if" statement in there it would be a lot simpler - 
> async code doesn't deal well with "if" statements I've found (there's no 
> way to short-circuit


But what about:


   - composition of async calls: asyncA(asyncB(asyncC(_), _), _)
   - chaining of async calls: asyncA(_).asyncB(_).asyncC(_)
   - async calls in expressions: asyncA(_) + asyncB(_), asyncA(_) && 
   asyncB(_)
   - async calls in array and object literals: [asyncA(_), asyncB(_)], { a: 
   asyncA(_) }
   - async calls in loop conditions: while (asyncA(_)) ...
   - try/catch/finally: try { asyncA(_); } catch (ex) { asyncB(_); } 
   finally { asyncC(_); }
   - async calls in switch (condition or branches).
   

Libraries like async help you with sequences of statements and with loops 
but they don't help you at all with any of these constructs. So you have to 
handle them by "disassembling" your code. Let's take chaining for example. 
First you disassemble asyncA(_).asyncB(_).asyncC(_) into:

var a = asyncA(_);
var b = a.asyncB(_);
var c = b.asyncC(_);
// more ...

and then rewrite with callbacks (I'm omitting error handling for clarity):

asyncA(function(err, a) { 
a.asyncB(function(err, b) { 
b.asyncC(function(err, c) {
// more ...
});
});
});

When I see asyncA(_).asyncB(_).asyncC(_) in a piece of code, I IMMEDIATELY 
understand what it means. Things are very different with the callback 
version above. I have to trace the parameters (a, b, c) to understand 
(verify) that this is actually chaining.

Note: the "use named functions" approach goes even one step further in the 
disassembly process. It feels like wiring the steps with gosub 
instructions. And it forces you to give names to things that don't deserve 
a name (because they are so specific).

There is nothing really "difficult" in the callback version above but it is 
definitely more tedious to write, more difficult to read and more error 
prone (especially once you add the error handling). 

And typical application code is full of simple logic that you would 
naturally write with these language constructs (if/else, composition, 
chaining, ...). Async libraries don't help you with these; you have no 
choice but disassemble. 

There is a pattern to handle each of these language constructs (even 
try/catch/finally!) so you could write some kind of super-async library 
that would cover the entire language. But such a library would be hard to 
learn and cumbersome to use. This is what I realized before writing 
streamline. This is why I abandoned the idea of a library and went with a 
preprocessor. 

(*) coroutines can solve this problem too (fibers, ES6 generators).

One last word about your error handling comment: the streamline version 
does the error checking. You don't see it but it does it. Errors are 
funneled to the magic `_` callback and express-steamline forwards them to 
next. Streamline lets you handle errors with try/catch so you don't need to 
check them in every call; you can catch globally.

Bruno

On Monday, September 22, 2014 3:18:40 AM UTC+2, Matt Sergeant wrote:
>
>
> On Sat, Sep 20, 2014 at 9:17 AM, Bruno Jouhier <[email protected] 
> <javascript:>> wrote:
>
>> How do you implement it with async?
>
>
> This is a good question, so here's an example translation (not the only 
> way to do it):
>
>
>     CDN.use(config.CDN.baseUrl, function(req, res, next) {
>         if(err) return next(err);
>         async.waterfall([
>             function (cb) {
>                 fs.lstat(config.base+"/cdn"+req.url, cb);
>             },
>             function (stats, cb) {
>                 if(stats.isSymbolicLink()) {
>                     async.waterfall([
>                         function (cb) {
>                             fs.readlink(config.base+"/cdn"+req.url, cb);
>                         },
>                         function (linkstr, cb) {
>                             log.info("Redirecting request \""+req.url+"\" 
> to \""+linkstr);
>                             fs.readFile(linkstr, cb);
>                         },
>                     ], cb);
>                 }
>             }
>         ], function (err, data) {
>             if (err) return next(err);
>             res.writeHead(200,{"Content-type":"text/html"});
>             res.end(data);
>         });
>     });
>
> It's more code, but it improves the error checking. If it didn't have that 
> "if" statement in there it would be a lot simpler - async code doesn't deal 
> well with "if" statements I've found (there's no way to short-circuit).
>
> Matt.
>

-- 
Job board: http://jobs.nodejs.org/
New group rules: 
https://gist.github.com/othiym23/9886289#file-moderation-policy-md
Old group rules: 
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 unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/nodejs/bae3f192-1769-4a62-b652-a7c61f06151d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to