63 comments

[ 1.9 ms ] story [ 176 ms ] thread
> 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() )
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?
If you are returning that promise, you'll need to handle both then and catch so the caller can chain their own catch.
A final .catch().then() swallows the error, .always() does not.
Catch "swallows" errors by design. Unless you re throw. chaining another then has no effect on the behavior of a previous catch.
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().

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.

If you rethrow in the catch the next then won't get executed.
Will finally be executed between the throw and whatever catches it or after?
Another 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;
      });
You 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`
Am 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.

The 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 {}.)

Thanks 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.

That'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.

That is assuming that you chain promises like

    a
    .then(b).catch(bhandle)
    .then(c).catch(chandle)
instead of

    a
    .then(aout => b(aout).catch(bhandle))
    .then(bout => c(bout).catch(chandle))
which admittedly is pretty ugly in javascript.
That’s not equivalent though — in the article’s example, imagine `element` is `undefined`, for example. `then()` wouldn’t be called in that case.
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.
Just use bluebird
Not really the ideal solution in a world where people have libraries that return promises and other libraries that operate on promises.
It'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.
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.
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
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.
Perhaps I was too terse in my comment and earned myself down votes.

Realistically, if you are shipping software in the real world where it could run on older browsers, you do need to use something like bluebird just to get uniform support. Not to mention things like maps/each/joins/timeouts that you will likely need for anything non-trivial.

What effect will .finally(...).finally(...) have ...?
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
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 [2]: https://github.com/tc39/proposals/blob/master/README.md [3]: https://tc39.github.io/process-document/

I believe stage 4 requires an actual implementation in a browser so it’s not surprising to see a feature ship before Stage 4.
Correct. By Stage 3, the proposal is supposed to be stable enough to start shipping it in stable browsers.
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?
It concerns promises.

And no, even exceptions are not "available on almost every other language".

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.

JavaScript does have a try / catch / finally in their exception handling. This update is for Promises which are a method of managing asynchronous code.
(comment deleted)
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()`

(comment deleted)
Despite the downvoting this is spot on. File in the (very large, now bulging) "What were they thinking??" file.
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;
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!)

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

JS didn't invent Promises/Futures nor did it invent Async/Await.
mostly true, but in which other language can you call async functions which you expect to be synchronous?
Not sure what you mean, do you mean what other language has "await"? Many.
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

If "element.textContent = error.message" throws an error, "hideLoadingSpinner()" won't get called (in the second example).
(comment deleted)
If "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.
Finally blocks ensure code executes even on exception. In the second example hideLoadSpinner will never get called if you hit the catch block
That's wrong- if the catch block runs without throwing any exceptions of its own then hideLoadSpinner will be called.
incorrect

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

If that's what the example is showing, then it's just obscuring two unrelated try/catch blocks. One for the outcome of the request, and one for the uncertainty of the element reference.
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')}})()

(comment deleted)
Finally.
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.
> 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/

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)
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.