A few years later, I use Promises a lot, and they work really well. The way errors bubble is logical and easily controlled, it's almost impossible to throw an unhandled promise exception, doing 'parallel' tasks is easy with ```Promise.all([])```, and there's pretty limited complexity with the way a Promise always returns a Promise.
I find the simplest way to use them is to define a class that has a few data properties you'd like to track.
class Handler {
constructor() {
// shared variables can be set / reset here
this.defineInitialState();
}
executeAction() {
return (
this.promiseFn()
.then(() => this.anotherPromise())
.then((passedArgument) => this.promiseExpectingArgument())
.then(
// success case
() => this.successHandler(),
(err) => this.errorHandler()
)
);
}
}
It's modular and you know what to expect (it's always a promise!). Sure, it's imperfect, but I think its weakly opinionated simplicity is a good tradeoff. After all, it makes no distinction of whether any of the functions in that chain were synchronous or not, and that's a nice thing to know. It also makes testing with dummy sync functions in place of actually asynchronous ones a 0-effort integration.I don't know about this async / await keyword stuff, and if you have to wrap it in a try/catch block, that's unfortunate but also seems like it's not the end of the world. After all, isn't the Go language constantly requiring you to say "if err != nil <your code>"? Explicit error handling isn't even a bad thing.
Anyways, this is just my opinion, it's fair to have your own, and maybe other languages handle asynchronicity more gracefully, but from what I've seen this is a clean way to wrap async OR sync code in a context that makes it always behave predictably and readably.