1. 17

  2. 31

    This post can be summed up by “I prefer promise syntax.” The argument that try / catch sucks but somehow Promise.then().then().then().catch() is better or easier to understand is subjective at best. I’ve never met someone who has written a lot of JS who prefers promise syntax to async/await, but I guess there’s a lid for every pot.

    1. 9

      I prefer promises to async/await. Async/await is a bad, leaky abstraction. For anything non-trivial you have to fall back to promises (eg Promise.all). If I’m going to have promises anyway, I might as well just use them everywhere. The other thing is that async/await doesn’t compose at all while promises do.

      async/await also directs the programmer towards inefficient code by default by encouraging sequential async operations (I see people writing code like that often).

      1. 15

        Promises themselves are a bad abstraction though.

        • They’re invoked immediately so you can’t build up a pipeline are pass them around easily and this causes a lot of devs to thunk their promises
        • Catching errors was never required so errors are often swallowed or are rethrown in a way that it’s hard to debug
        • Cancelation had to be added way later than it should have been (and many folk had warned during the proposal phase) and as a result we have this bad API for AbortControllers
        • all vs allSettled should have been there from the get-go instead added of years later
        • Folks realized it’d be nice to have a do-like notation so async & await were rushed out without considering the ergonomics for the parallel APIs

        What JavaScript wanted was an asynchronous Task and could have been copied from established languages, but what we got was Promise. We’d be better off normalizing picking for our projects one of the Fluture library, fp-ts’s Task, Aff from PureScript, or whatever the API is called in effect-ts (they are all close to the same thing).

        1. 3

          Totally agree! It’s just that async/await is even worse, and all I have to fall back on is promises.

        2. 3

          Async/await are promises. It doesn’t have to be an either/or choice.

          1. 2

            That’s precisely the problem. Is there any value in a tiny bit of syntactic sugar that also encourages inefficient code? In my view, the value is negative.

            1. 1

              A lot of the code where this would be used is going to have branching logic and overall not be terribly long anyways, so the emphasis on inefficiency doesn’t really ring true to me.

              I will admit to not knowing the intricacies of javascript optimization, but I fail to see how using a syntactic sugar would impact it at all.

              This whole discussion sounds like like arguing between the do syntax and the bind operator in Haskell. I for one prefer my monads easier to read. As code should be for reading first and compilers second.

              The only thing I will concede is that I have seen developers try to break out of the asynchronous context by assigning variables in the synchronous context. This might be worse in rxjs though. The use of Promise directly does force one to read it as asynchronous compared to missing the async keyword.

          2. 2

            They are both silly and limited. They both rely on. Funny looking code. They both introduce disturbance the code execution thredline rather then logically allow the programmer to control when an arbitrary code block is executed. We have to really on somewhat funny syntax.

            Both of them are limited to “execute this on the side and when you are ready execute that. But there are no means of controling concurrency. If you want to do thousands of instances of something, both APIs become essentially fork bombs.

            Dijkstra wrote down a blueprint for intuitive concurrency (P() and V()) which has been used in many languages for decades. I don’t understand why we have those funny looking things instead of what is tried and true.

            Even python went down the path of async wait. The result is a gigantic divider on any python codebase that uses. Any and every little IO operation becomes subject to this great divide. Feels like each time a line if code with IO on it comes, there’s a gun pointed to the head asking: sync or async?

            1. 2

              Sure, I agree that both mechanisms are bad, it’s just that async/await is worse. I much prefer the approach to concurrency taken by Elixir/Erlang. I don’t think, however, that semaphores by themselves are the only mechanism we need.

              1. 1

                What do you need more than a semaphore and a method to fork execution in two? You could trivialy implement a pool with it.

              2. 1

                Dijkstra wrote down a blueprint for intuitive concurrency (P() and V()) which has been used in many languages for decades. I don’t understand why we have those funny looking things instead of what is tried and true.

                P/V models a specific concurrency primitive usually known as a semaphore, right? As far as I understand them, semaphores are a useful tool, but don’t represent a solution to concurrent programming in general — am I mistaken?

                1. 4

                  You can model any concurrency problem using semaphores. That doesn’t mean you want to.

                  1. 2

                    It has been very long since I have red the paper. I don’t remember if it explicitly mentioned that some means of parelelize execution is needed at some point or that was assumed. Fork() spawn(), thread.new(). Whatever it looks like. The problem with old school multi process or multi threading was not the API, but rather the overhead. There is no reason why such an API could not be used with say, green threads. Eventlet does just that.

              3. 8

                Yeah this whole article reads like the author wanted to say “I find async/await new and unsettling” without saying those words. And the parallel example feels like a strawman: why would async/await make you forget that you want to wrap parallel work in Promise.all?

                I think it’s fine to say “I prefer the old ways” without needing some objective justification.

                1. 1

                  I think it’s fine to say “I prefer the old ways” without needing some objective justification.

                  In a professional setting, you need objective decisions to convince people. It’s very difficult to use your own preferences to change the style of other authors.

                  1. 4

                    Totally. This article is not that, tho.

                2. 1

                  The post cannot be summed up that way.

                3. 13

                  To me this is a completely different problem of async/await or promises.. Its, should we learn our underlying abstractions, or force explicitness to be a reminder (not even enforcer in this case) AND where is the line.

                  To me the big problem with promises if you have to keep track of more than 1 variable. You end up keeping this intermediate objects around, which can lead to logic bugs which I’m more concerned about than forgetting about Promise.all, or other optimizations.

                  For example

                  let user = await fetch(userEndpoint).then(res => res.json())
                  let books = await fetch(bookEndpoint, { userId: user.id }).then(res => res.json())
                  return { userName: user.name, books: books}
                  // vs
                  return fetch(userEndpoint).then(res => res.json())
                    .then(user => fetch(bookEndpoint, { userId: user.id })
                       .then(res => res.json())
                       .then(books => ({ books, user })
                    .then(({ books, user }) => ({ userName: user.name, books })

                  That intermediate indentation & object is necessary & only gets more messy with more async variables.

                  Syntax (when done right) matters in that it can lessen (or increase) our cognitive load at any given moment. It allows us to break things down into chunks, or conflate things together that don’t necessarily need to be.

                  1. 5

                    Something about async/await is nice but it’s not like it solves any major problems. In fact, isn’t that exactly how it’s marketed, as syntax sugar?

                    For both sides of this debate, there are a million arguments for or against what’s more ‘readable’ but it’s subjective, and even if it weren’t the difference is fairly minimal. So I’m not persuaded to avoid either way really.

                    1. 7

                      I think async/await syntax is objectively more readable. It’s less indentation and less context you need to keep in your head while observing a given piece of code.

                      1. 1

                        It rather depends on how you write it. I’ve seen promise chains that read like a beautiful DSL and promise chains that would make you weep in horror; I’ve seen async/await used to elegant effect and async/await written like someone wanted to use exceptions as a tool for murder.

                        There’s nothing objective about it, any more than imperative code is “objectively” more readable than FP code. Background and local style both dominate other effects,