News for hackers. Stuff that matters.
Re:Node v7.5.0 Released by Bahamut on Friday February 03, 2017 @07:03AM (#13559204)
I highly recommend against using that library - it encourages a lot of callback hell we've found at my company, and it is much harder to create nice reusable functions with it vs. promises. bluebird is a wholly superior library for handling async flow.
Re:Node v7.5.0 Released by nailer on Friday February 03, 2017 @08:16AM (#13559560)
> it encourages a lot of callback hell we've found at my company

This is a very odd comment. async provides the same level of indentation as Promises does:

  async.waterfall([
    function(){},
    function(){},
  ])
Here's promises doing the same thing:

  function()
  .then(function(){})
  .then(function(){})
One's a list of functions to be run in order, the other uses method chaining. You may prefer one or the other but saying async 'encourages callback hell' is about as logical as saying promises does.

__Edit__: further to my own comment. The people I know who know their stuff (ie, work on JS itself) and like/prefer Promises do so because they believe that functions should return values.

I don't think anything will be better than either solution until async/await gains wide support. Async/await needs return values, so hence promises, so it's definitely worth knowing promises. But yeah, right now async is fine.

Re:Node v7.5.0 Released by Matthias247 on Friday February 03, 2017 @10:35AM (#13560463)
Promises also work better with Typescript, since it's well defined which type each step returns and which the next function expects. That means the .then(a => next(a)) cascades can be typechecked well. async.waterfall doesn't seem to be typecheckable.
Re:Node v7.5.0 Released by nnq on Friday February 03, 2017 @12:47PM (#13561620)
The huuge difference once you understand its implications is that you can:

    return function().then(() => ...).then(() => ...)
...and the caller of you function can decide to chain even more stuff after, in the simplest case. Most obvious benefits, though not the biggest, is that you simply can return a promise ad drop the ugly extra callback argument that you function needs to have. For example:

    function foo(cb) {
        async.waterfall([
            function(){},
            function(){},
        ])
    }
becomes:

    function foo2() {
        return function()
            .then(function(){})
            .then(function(){});
    }
...and this way the logic ends up where it should be, in the caller, not the callee!

Another simple to explain benefits are the fact that you can .then() a promise 10 times and what it does will only happen once, and then the other times you'll just consume the result. You don't need to care if something is (a) already computed, (b) in the process of being computed, (c) scheduled for computation later etc., you can write the exact same code for all cases. Because you're working with values that have dependencies between this, not with processes that need to happen in a certain order.

The bigger benefits show up when you write more and more code and you see how beautifully it all untangles with promises.

And the fact that once you learn promises you can carry further the knwledge to other async patterns like co and async/await (they are all understandable in terms of promises).

Do yourself and everyone you work with a favor and move away from async please, especially in a team promises make everything so much easier to understand! Same advice of you're writing nodejs libraries: have you functions return promises instead of take callbacks! Drop the intellectual lazyness and grow beyond "async by callbacks", it's the worst coding style ever. And everyone will be able to convert almost instantly from promises to async/await since the logic is similar, just syntactic sugar comes over it.

Re:Node v7.5.0 Released by nailer on Friday February 03, 2017 @01:00PM (#13561736)
As I said: promises return values. That's great. But describing callbacks as "worst coding style ever" and "intellectual lazyness" is hyperbole to describe a style that most of the JS community uses.

Also: not sure if you mean 'you' singular or 'you' plural, but personally I use a combination of callbacks for node stuff with basic async patterns applied (not the 'async' module) and promises.

Re:Node v7.5.0 Released by nnq on Friday February 03, 2017 @02:05PM (#13562293)
The hyperbole is not my style actually, but I use it when talking about JS because I've came to realize the in the JS community it's the only way to be heard.

Generally I see the JS async styles continuum as: (0) callbacks < (1) async module < (2) promises < (3) co < (4) real async/await when language supports it, and I advocate skipping the intermediate steps in this continuum, (1) and (3) because they can lead to either badly composable code or subtle bugs. Also, I stressed the "intellectual lazyness" label because I know lots of people learn async module and this, "ok, this is good enough, I'll stop learning new things about writing better async code in JS".

...and I'm ok with good ol' callbacks where it makes sense for performance reasons, like core part of web frameworks or game engines (even if there is no sensible speed reduction, there is a memory overhead from creating a zillion promises).

Re:Node v7.5.0 Released by mattnewton on Friday February 03, 2017 @10:08AM (#13560238)
It also provides more specific control flow functions that are bug-prone to roll your own with promises. I've learned a lot by looking at the source!
Re:Node v7.5.0 Released by chadscira on Friday February 03, 2017 @11:21AM (#13560853)
Yea but when using promises with async/await its very beautiful.

    await asyncFuncOne();
    await asyncFuncTwo();
No more weird water-falling, and finally proper exception handling. The only gotcha is the fact that we cant awaits at the root of a module, and we need to wrap it in a async function.
Re:Node v7.5.0 Released by wildpeaks on Tuesday February 07, 2017 @11:24PM (#13595736)
The 250Kb extra weight to the Babel bundles (compared to the equivalent callback version) is quite frustrating though.
Re:Node v7.5.0 Released by increment_i on Friday February 03, 2017 @08:45AM (#13559707)
I use Node a lot, and I find myself reaching for the async library any time the project reaches a non-trivial size and there's a lot of communication going on between services. I don't find it all that hard to read (the async.waterfall method looks somewhat like promises chaining) I suppose I should spend a weekend or two steeping myself in promises and es7. It's mildly annoying to have to learn a new async paradigm every couple years, and like others have pointed out, the Node core hasn't changed - its still callbacks.
Re:Node v7.5.0 Released by zachrose on Friday February 03, 2017 @07:54AM (#13559433)
So much of the promise-based code I see looks almost the same as it would with plain callbacks, just with the callbacks plopped into .then(). Am I missing something? How do promises make for more reusable functions?
Re:Node v7.5.0 Released by vkjv on Friday February 03, 2017 @08:08AM (#13559515)
In my opinion, these are the major ones:

1. Promises are a value. They can be passed to other functions. This simplifies a lot of control flow.

2. The `.then` method on a promise is similar to `flatMap` in other languages / libs. The next chain will wait for any nested promises to complete. This flattens callbacks so you can see the flow instead of the pyramid of doom.

3. Native promises include a bit of sugar, such as Promise.all. You need a library to do this with callbacks.

4. Promises are the backbone of async/await. You need them to use it.

Example:

    await const [a, b] = Promise.all([getA(), getB()]);
Re:Node v7.5.0 Released by jcheng on Friday February 03, 2017 @10:58AM (#13560669)
5. Error handling and even successful termination is very, very easy to get wrong with nested callbacks (forgetting to call a callback, or forgetting to catch unexpected exceptions and pass them to the callback, means the function never returns at best or brings down the whole process at worst). Promises make this much easier by automatically propagating errors for you down the promise chain (though you or your caller do still need to handle the error at the end of the chain).
Re:Node v7.5.0 Released by rictic on Friday February 03, 2017 @12:14PM (#13561345)
Nit, but await is an operator, not an annotation on a statement. So that should be written as:

    const [a, b] = await Promise.all([getA(), getB()]);
Re:Node v7.5.0 Released by disease on Friday February 03, 2017 @09:34AM (#13560016)
I'm surprised no one has mentioned my favorite aspect of Promise, which is the fact that you can plop a .catch at the end of a chain and it will do the error handling there regardless of where in the chain it failed. Doing this with long chains of asynchronous calls before Promise was a huge pain in the ass.
Re:Node v7.5.0 Released by thealfreds on Friday February 03, 2017 @08:13AM (#13559550)
Chaining and returning can keep things nice and neat. Say you are following a Controller -> Service -> Repo implementation.

Controller calls the service.

Service returns a call to a repo with any mapping that needs to occur.

Repo calls out to database or external api and returns the promise.

Makes the work very boring and repetitive for the most part which is a good thing I think.

    function someController(req, res) {
        UserService.getUser(req.session.user)
            .then(SomeService.getSomething)
            .then((val) => res.json(val))
            .catch(handleError);
    }

    function getSomething(user) {
        return SomeRepo.getSomething(user)
            .then(MapSomething)
            .catch(alternativeHigherLevelCatch);
    }

    function getSomething(user) {
        return new Promise((resolve, reject) => {
            database.getSomething(
                'defined parameters',
                user,
                resolve,
                reject
            )
        });
    }
Re:Node v7.5.0 Released by rhinoceraptor on Friday February 03, 2017 @11:24AM (#13560885)
It's trivial to refactor that code to use only callbacks, without ending up with callback hell.

You certainly want to call UserService.getUser for many different controllers. Why manually do that for each controller? Simply make a middleware to get the user.

If you take this to its logical conclusion, you end up with small, composable middleware functions, and controllers that are pure functions.

Re:Node v7.5.0 Released by thealfreds on Friday February 03, 2017 @11:53AM (#13561147)
Well sure with a single service and a single mapper etc. Once you start tacking on multiple it keeps following the pattern above with promises and just extends with `.then`'s.

Also agree middleware is a good solution which can go through and provide a user object directly onto the request or another level of abstraction created if you prefer. That was simply to show how multiple service calls could be easily tied together rather than a full and usable example.

A benefit of callbacks though, at least from my experience, is error messages when testing with mocha / chai. I only have experience with jasmine, mocha, and chai but the errors when test driving were much easier to follow with callbacks.

Re:Node v7.5.0 Released by latch on Friday February 03, 2017 @08:07AM (#13559509)
Because promises are chainable, it can take N level-deep code down to 1 level deep. So that's your first big win.

The second one is that you can await a promise, which turns your 1-level-deep code to 0 levels deep.

Re:Node v7.5.0 Released by taternuts on Friday February 03, 2017 @01:48PM (#13562132)
I wouldn't necessarily say they make for more reusable functions. I think primarily their purpose is to provide a cleaner abstraction for dealing with async code flow. Plus I just love seeing code like:

doSomething.then(stepTwo).then(stepThree).catch(errHandler);

Re:Node v7.5.0 Released by zachrose on Friday February 03, 2017 @10:17AM (#13560299)
I should have added that I'm asking to compare the special parts (e.g. reduce, waterfall, etc) of Bluebird vs the async library.
Re:Node v7.5.0 Released by egeozcan on Friday February 03, 2017 @08:02AM (#13559480)
You can take the part before `.then()` and pass it around.
Re:Node v7.5.0 Released by woudsma on Friday February 03, 2017 @08:09AM (#13559517)
yes that works quite great. you have to define a 'async' function, in which you can use the 'await' keyword.

async getDoc(doc) {

  const result = await db.get(doc)

  console.log('result:', result)
}

// instead of:

function getDoc(doc) {

  db.get(doc, result => {
    console.log('result:', result)
  });
}

// or

function getDoc(doc) {

  db.get(doc)
    .then(result => 'result: ' + result)
    .then(::console.log)
    .catch(::console.error)

}