
Promise.prototype.finally - stablemap
https://developers.google.com/web/updates/2017/10/promise-finally
======
runarberg
The 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/)

~~~
styfle
I believe stage 4 requires an actual implementation in a browser so it’s not
surprising to see a feature ship before Stage 4.

~~~
mathias
Correct. By Stage 3, the proposal is supposed to be stable enough to start
shipping it in stable browsers.

------
drinchev
> 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
Yes, 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?

~~~
untog
A final .catch().then() swallows the error, .always() does not.

~~~
joshribakoff
Catch "swallows" errors by design. Unless you re throw. chaining another then
has no effect on the behavior of a previous catch.

~~~
untog
Right, 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
Yea, 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.

------
dlbucci
It 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.

------
shp0ngle
I 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

~~~
tantalor
In this case, if the error object is undefined/null, then error.messsage
throws a TypeError, and the spinner is not hidden.

In general the finally { ... } block is useful because it is guaranteed to
execute after the try/catch (if the program has not exited), where as the next
line after the try/catch is not, e.g., the try/catch returns, or the catch
block throws.

~~~
l1ambda
Example:

(()=>{try {return 42} finally {console.log('you will see this')}})()

The fn body returns 3, but also the "finally" executes first and you get the
console.log. Same with:

(()=>{try {throw 42} finally {console.log('you will see this')}})()

------
vdnkh
> 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
The 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
I'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.

------
matt_wulfeck
I 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.

~~~
singularity2001
JavaScript 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()`

~~~
Touche
No, 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
ok and to be safe you ``` await every() await freakin() await function() ``` ?

Or you may end up with obscure bugs, when you expect a sync method or worse,
when a sync method changes to async in the next api version(happened in real
world!)

~~~
Touche
I'm not aware of any programming language where it is safe to use a function's
return value without knowing what the return value is. This seems unrelated to
async/await, if you are calling functions that are returning things other than
what you expect, you are pretty much screwed.

~~~
xfer
No in typed language, this is not a problem because the compiler will refuse
to compile. For uni-typed languages async/await is not the right model imho.

~~~
Touche
Again, this is unrelated to async/await. Normal functions might return a
number or an object or undefined or whatever. Since JavaScript is a dynamic
language it _is_ unsafe to call a function without either 1) Trusting that the
function returns what its API says it should return or 2) Checking the return
type and acting appropriately.

So, the grandparent's complaint is about JavaScript being a dynamic language,
async/await doesn't fix this issue or make it any worse.

~~~
singularity2001
grandpa here. position accepted.

------
breatheoften
What effect will .finally(...).finally(...) have ...?

~~~
runarberg
I 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

------
Marazan
Finally.

~~~
dwaltrip
Hah. 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.

------
psadri
Just use bluebird

~~~
untog
Please 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
Comparing 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
Sure, 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.

