13 ms·
Promise.prototype.finally
- drinchev 9y ago> In either case we need to call hideLoadingSpinner(). Until now, we had no choice but to duplicate this call in both the then() and the catch() block. I always chain a `then` after `catch` in these situations. fetch(...) .then( response => data.response = response ) .catch( error => data.error = error ) .then( () => closeThatLoader() )
- Kiro 9y agoYes, trying to understand what the point of .finally is when a subsequent .then after .catch seems to achieve the same thing. What am I missing?
- eknkc 9y agoIf you are returning that promise, you'll need to handle both then and catch so the caller can chain their own catch.
- untog 9y agoA final .catch().then() swallows the error, .always() does not.
- joshribakoff 9y agoCatch "swallows" errors by design. Unless you re throw. chaining another then has no effect on the behavior of a previous catch.
- untog 9y agoRight, but in the context of the OP's question you'd need the second .then() to mirror what .always() is doing. The point is, there is no way to do .catch().then() and throw the error unless you do some hideous type checking hack cludge. Hence the need for .always().
- JSDave 9y agoYea, the `catch` would probably have to set an 'errors' object on the response. The following `then` would then decide whether or not to throw an error based on that. Not pretty. Also, I think `finally` makes your code easier to read. I always put the same kind of cleanup logic in there, everyone on the team knows what to expect in that block.
- bbatha 9y agoIf you rethrow in the catch the next then won't get executed.
- Kiro 9y agoWill finally be executed between the throw and whatever catches it or after?
- runarberg 9y agoAnother upside of the proposal is that if you need to reuse the fetch promise somewhere else it will retain the reject status if the promise fails. The proposal is more akin to: fetch(...) .then(response => { closeThatLoader(); return success(response); }) .catch(error => { closeThatLoader(); throw error; });
- riquito 9y agoYou may want to do something _and then_ throw the error, to let it reach the caller. In your example it would mean to run "closeThatLoader" and later throw the error. It's really cumbersome without `finally`
- semperdark 9y agoAm I the only one that really dislikes the "catch" behavior? The fact that catching a promise marks it as resolved and triggers subsequent "then"s rather than "catch"s. It means that every callback needs to be aware of every other callback and their own place in the order, so that only the last catch statement doesn't rethrow the exception. Something like $.Deferred() allows you to set 10 "fail" callbacks independently, which helps a lot when the original promise is generated by a deeply nested method and different stages of the chain add their own handling.
- WorldMaker 9y agoThe catch behavior mirrors try {} catch {} blocks. If you don't need a catch, don't catch, and let it bubble up. Try to keep the things you catch to a bare minimum. The rethrow decision is largely the same for Promises as try {} catch {} blocks, too. (It's more apparent too in the async/await space where catch is even literally try {} catch {}.)
- semperdark 9y agoThanks for the reply. I guess my contention as someone just starting to use vanilla promises is that it feels like try/catch isn't the appropriate metaphor to use here. In my specific case I'm passing a lot of promises to user-made scripts. then/catch seems to make things more brittle. As I said though, I'm new to them.
- WorldMaker 9y agoThat's totally fair. There are learning curves involved and sometimes tradeoffs aren't entirely obvious why they were made. If you get a chance, particularly play with async/await code somewhere. There's a lot of misconception that async/await replaces Promises or is very different from Promises, but the reality is much closer that Promises were a necessary building block for async/await, and some of the trade-offs in Promise design are there to support async/await, based on work that had already been done in other languages. Maybe if you try to write some of your work as async/await it might give you insights into your architecture and how to make it feel less brittle.
- mathias 9y agoThat’s not equivalent though — in the article’s example, imagine `element` is `undefined`, for example. `then()` wouldn’t be called in that case.
- dlbucci 9y agoIt kind of drives me nuts that this wasn't there to begin with. It seems like it was a standard part of most promises before they were standardized, but now all promise code will either need to ship with a finally shim or avoid finally for the next X years.
- psadri 9y agoJust use bluebird
- jameshart 9y agoNot really the ideal solution in a world where people have libraries that return promises and other libraries that operate on promises.
- iEchoic 9y agoIt's really easy with Bluebird to assimilate promises from other libraries, though. Right now, I still think it's best to use Bluebird from the start in a new project than to use native promises. They're faster, have better error stack traces, and more capabilities.
- untog 9y agoPlease don't. According to the docs Bluebird is 17.76KB gzipped. With extensive Promise support in browsers that's a total waste of bandwidth and parse time.
- joshribakoff 9y agoComparing bluebird and promises is like comparing squares to rectangles. It's a superset. One benefit of bluebird is having specific catch blocks for specific types of errors. You can have certain errors handled by the catch while other errors bubble through. This is also possible (and cleaner) with async/await though. Bluebird also adds a bunch of operators for racing promises a d and polyfills for other things that might not be available in your runtime. I'm not saying to use bluebird, I definitely prefer native implementation, but there are still contrived use cases for bluebird
- untog 9y agoSure, but don't "just use Bluebird", as the OP said, in every situation. If you require the extra features of Bluebird, use Bluebird. But if you're just handling simple Promises, it's a waste.
- breatheoften 9y agoWhat effect will .finally(...).finally(...) have ...?
- runarberg 9y agoI suppose it will simply add two side-effects to the last link in the promise chain: $ node --harmony_promise_finally -e ' Promise .resolve() .finally(() => console.log("foo")) .finally(() => console.log("bar")); ' foo bar
- runarberg 9y agoThe actual proposal[1] is currently on stage 3 on the standards tract[2]. Meaning that the spec is complete and further refinement will require feedback[3]. Should I be a little surprised to see this enabled by default before it reaches stage 4 (ready for inclusion in the next version of the standard)? [1]: https://github.com/tc39/proposal-promise-finally https://github.com/tc39/proposal-promise-finally [2]: https://github.com/tc39/proposals/blob/master/README.md https://github.com/tc39/proposals/blob/master/README.md [3]: https://tc39.github.io/process-document/ https://tc39.github.io/process-document/
- matt_wulfeck 9y agoI don’t really get JavaScript. Isn’t this type of try/catch/finally method available on almost every other language? Is JavaScript behind the times or is this just not a common use-case?
- coldtea 9y agoIt concerns promises. And no, even exceptions are not "available on almost every other language".
- matt_wulfeck 9y agoI don’t really get JavaScript. Isn’t this type of try/catch/finally method available on almost every other language? Is JavaScript behind the times or is this just not a common use-case? Personally I like the “defer” method, where closing actions can be clear and front-loaded in the function definition.
- BinaryIdiot 9y agoJavaScript does have a try / catch / finally in their exception handling. This update is for Promises which are a method of managing asynchronous code.
- deleted 9y ago[deleted]
- singularity2001 9y agoJavaScript invented a new concurrency model which IMHO is worse than the invention of 'goto', by putting async mechanisms on the callee side: `result=mayBePromiseOrResultWhoKnows()` instead of `go nowThisSyncMethodIsAsync()`
- deleted 9y ago[deleted]
- dboreham 9y agoDespite the downvoting this is spot on. File in the (very large, now bulging) "What were they thinking??" file.
- Touche 9y agoNo, the caller has control. result = await maybePromiseWhoCaresIUsedAwait(); You can use await without knowing if the function you called returns a promise or not. This works fine: result = await 3;
- singularity2001 9y ago
- shp0ngle 9y agoI do not undetstand the async example. What is the difference between try { const response = await fetch(url); const text = await response.text(); element.textContent = text; } catch (error) { element.textContent = error.message; } finally { hideLoadingSpinner(); } and try { const response = await fetch(url); const text = await response.text(); element.textContent = text; } catch (error) { element.textContent = error.message; } hideLoadingSpinner(); ? Other than that, this is great. But I don't understand the async/await version
- olalonde 9y agoIf "element.textContent = error.message" throws an error, "hideLoadingSpinner()" won't get called (in the second example).
- deleted 9y ago[deleted]
- stefano 9y agoIf "element" is undefined or null, the catch clause will raise an exception. In the first example, hideLoadingSpinner() will be called anyway, while in the second one it won't.
- slackingoff2017 9y agoFinally blocks ensure code executes even on exception. In the second example hideLoadSpinner will never get called if you hit the catch block
- Rusky 9y agoThat's wrong- if the catch block runs without throwing any exceptions of its own then hideLoadSpinner will be called.
- GroSacASacs 9y agoincorrect try { throw new Error("catch me !"); // force try to fail } catch (catchedError) { console.log("inside catch"); // throw new Error("hmm"); } finally { // always executed console.log("inside finally"); } // always executed console.log("after try catch finally"); the finally statement can be useful when another error is thrown inside the catch block, in which case you have more serious problems in your code
- Marazan 9y agoFinally.
- dwaltrip 9y agoHah. I actually expected this to be a post (with a clever title) about how all of the leading browsers have finally provided a native implementation of the Promise API.
- vdnkh 9y ago> Since async and await are strictly better, my recommendation remains to use them instead of vanilla promises Let's not assume that polyfills are free. As far as I can tell [0], you need two Babel transforms to enable support across browsers, with the generator polyfill running you about 20kb. [0] https://babeljs.io/docs/plugins/preset-es2017/ https://babeljs.io/docs/plugins/preset-es2017/
- maxfurman 9y agoThe article that the author links in that sentence goes into more depth; it claims that async/await is better in part because V8 can optimize it better than it can Promises. In other words, async/await is better than Promises, provided that you are not using polyfills. He isn't assuming polyfills are free, he is assuming that you don't need them (or that you can pull them out of your bundle over time)
- maxfurman 9y agoI'm shocked that `.finally` wasn't part of the standard in the first place. Every promise library I've used (angular 1's $q and bluebird mostly) has had a finally method. Better late than never, I suppose.