5 ms·
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 fu
by Bahamut 10y ago
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.
- zachrose 10y agoSo 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?
- egeozcan 10y agoYou can take the part before `.then()` and pass it around.
- woudsma 10y agoyes 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) }
- latch 10y agoBecause 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.
- vkjv 10y agoIn 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()]);
- jcheng 10y ago5. 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).
- rictic 10y agoNit, 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()]);
- thealfreds 10y agoChaining 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 ) }); }
- rhinoceraptor 10y agoIt'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.
- thealfreds 10y agoWell 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.
- disease 10y agoI'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.
- zachrose 10y agoI should have added that I'm asking to compare the special parts (e.g. reduce, waterfall, etc) of Bluebird vs the async library.
- taternuts 10y agoI 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);
- nailer 10y ago> 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.
- Matthias247 10y agoPromises 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.
- mattnewton 10y agoIt 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!
- chadscira 10y agoYea 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.
- wildpeaks 10y agoThe 250Kb extra weight to the Babel bundles (compared to the equivalent callback version) is quite frustrating though.
- increment_i 10y agoI 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.