6 ms·
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 somet
by zachrose 10y ago
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?
- 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);