I get it. I do plenty of async programming. But, at the end of the day, no matter how nice you think the async.waterfall mess is, sometimes you just want to block. Sometimes it turns out that that is the right pattern. I've written dozens of async apps, much to the chagrin of co-workers who hate anything resembling async. I think I'm qualified to know that sometimes I get to a bit of code that sure would be nice with a simple blocking call.
async.waterfall([ function getInput(){...}, function checkInput(){...}, function submitInput(){...}, ], function finally(){})
Each of the functions takes a callback, and returns err (if any) and output. If a callback returns err, it jumps to finally where you say what failed.
This is odd when you first get started with async, but it's really easy to visualize.
Think of a production line, with a number of different workers doing different stuff. If one of them gets a dud part, it throws it away.