News for hackers. Stuff that matters.
Re:I Avoid Async/Await by chmod775 on Saturday April 16, 2022 @06:57AM (#31051360)
First off, async/await is promises. It's merely syntactic sugar. The point of async/await is not to never have the word "Promise" appear in your code. It's also not meant to be universally better than using Promises bare-bones.

A lot of the argument appears to be the author extrapolating from his own lack of familiarity to others: "We are taught", "our minds", etc. I can easily construe some hypothetical person with a certain set of skills (and lack thereof) that would have just as much trouble with bare-bones Promises. But I don't have to because the author did it for me at "One more thing…".

"People can mess this up" was never an argument. You can mess everything up. The more interesting question is how badly and how often.

Re:I Avoid Async/Await by gotaquestion on Saturday April 16, 2022 @12:43PM (#31053809)
> A lot of the argument appears to be the author extrapolating from his own lack of familiarity to others

This is literally 97.5% of all tech blogs, and a disappointing amount of content on HN front page.

I feel bad for the author. Nothing like asserting your ignorance on a blog. There's a time and a place for literally every language construct.

Re:I Avoid Async/Await by gompertz on Saturday April 16, 2022 @01:05PM (#31053998)
Agreed, and I think some authors need to be more humble. If a construct or design choice exists, its far more likely someone (or a team) spent hundreds more hours intentionally reasoning for its existence in the first place; over your passive duration of usage.
Re:I Avoid Async/Await by Waterluvian on Saturday April 16, 2022 @07:34AM (#31051520)
TypeScript even forces you to acknowledge this as the explicit return type of an async function is a promise.
Re:I Avoid Async/Await by spankalee on Saturday April 16, 2022 @12:58PM (#31053942)
async/await is _safer_ too, because part of that syntactic sugar is wiring up exceptions correctly and ensuring that a function only returns or rejects asynchronously and never synchronously. This prevents lots of bugs.

This article is bad, bad advice.

Re:I Avoid Async/Await by eurasiantiger on Saturday April 16, 2022 @07:10AM (#31051415)
This, very much this.

    async function() {}
is much nicer than

    function() {
      return new Promise(
        (resolve) => resolve()
      )
    }
Re:I Avoid Async/Await by dataflow on Saturday April 16, 2022 @08:14AM (#31051687)
new Promise() is not how you want to create a promise in JS (except when you really have to), just like 'new'/'delete' are not how you ant to allocate memory in C++ (except when you really have to). There are lots of helpers in that class that make promises significantly more ergonomic.
Re:I Avoid Async/Await by michaelsalim on Saturday April 16, 2022 @08:22AM (#31051729)
Care to give some examples of the helpers? I always just use new Promise()
Re:I Avoid Async/Await by lmc on Saturday April 16, 2022 @12:14PM (#31053511)
Here's a few. The docs link to some others that may also be useful...

Promise.resolve()

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

You can use this in place of new Promise() (though you rarely need it, as an async function automatically wraps any non-promise return value in a promise.)

----------

Promise.all()

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

For making requests in parallel, and returning when all have been successful.

----------

Promise.allSettled()

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

For making requests in parallel, and returning when all have completed whether successful or not.

----------

Promise.any()

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

For making requests in parallel, and returning the first successful response.

--------

As mentioned in sibling comment, things like fetch and DB calls return promises anyway, so the above are mostly only useful for working with multiple other promises.

Re:I Avoid Async/Await by aGHz on Saturday April 16, 2022 @08:31AM (#31051782)
You really end up creating promises manually, the vast majority are downstream from an IO call like fetch() or a database query.
Re:I Avoid Async/Await by eurasiantiger on Saturday April 16, 2022 @09:48AM (#31052294)
Many devs unnecessarily nest Promises like that.

    async function getData() {
      return new Promise((resolve) => {
        const res = fetch(…);
        resolve(res);
      });
    }
The response above is wrapped in THREE different Promises! One from fetch, one manually created, and one implicitly created by `async`.

The code above behaves exactly the same as

    function getData() {
       return fetch(…);
    }
or even just

    fetch(…);
Re:I Avoid Async/Await by RobertWHurst on Saturday April 16, 2022 @10:53AM (#31052785)
I've been on teams working on node projects for like 15 years. I've never seen someone who understands promises or async await do that. You'd have to know nothing about what the async keyword actually does to be compelled to do that.
Re:I Avoid Async/Await by hn_throwaway_99 on Saturday April 16, 2022 @11:55AM (#31053314)
I mean, I know its a bit of linguistic flourish, but considering NodeJS was only initially released 12 years ago, that's quite a feat!
Re:I Avoid Async/Await by eurasiantiger on Saturday April 16, 2022 @11:12AM (#31052937)
I have seen plenty of (usually junior front-end) devs resort to exactly this kind of cargo cult coding and magical thinking.
Re:I Avoid Async/Await by wiml on Saturday April 16, 2022 @11:38AM (#31053152)
Well, they're junior devs. You're supposed to help them get past that stage, not just throw away all your tools.
Re:I Avoid Async/Await by eurasiantiger on Saturday April 16, 2022 @05:30PM (#31056257)
What made you think I haven’t been helping them?
Re:I Avoid Async/Await by joe_guy on Saturday April 16, 2022 @11:40AM (#31053179)
I'm going out on a limb here and assuming the issues with their code don't begin and end with async.
Re:I Avoid Async/Await by cercatrova on Saturday April 16, 2022 @11:54AM (#31053301)
> I've never seen someone who understands promises or async await do that. You'd have to know nothing about what the async keyword actually does to be compelled to do that.

> usually junior front-end

So you agree with each other then?

Re:I Avoid Async/Await by eurasiantiger on Saturday April 16, 2022 @03:12PM (#31055170)
Good day, axe handle.
Re:I Avoid Async/Await by Cpoll on Saturday April 16, 2022 @12:13PM (#31053492)
> function getData() { return fetch(…); }

I feel like there are advantages to making it `async function`, even if it's superfluous, because it signals to readers and to static code analysis that the function returns a promise. That's assuming the return type of fetch(...) can't be inferred by static analysis and developer tooling.

Re:I Avoid Async/Await by Kwantuum on Saturday April 16, 2022 @12:25PM (#31053629)
> the type of fetch() can't be inferred by static analysis

That is preposterous.

Re:I Avoid Async/Await by Cpoll on Saturday April 16, 2022 @01:00PM (#31053950)
It's not. One might assume it's Node fetch, but parent post was just using a specific example to make a general point. Fetch can be `any`thing.

Would you say the same thing about

function getData() { return mysteryFunction(…); }

Re:I Avoid Async/Await by garyrob on Saturday April 16, 2022 @12:19PM (#31053558)
I'm a newbie with respect to JS and especially promises and async/await, but I need to learn. If you could point me to some resource that does a really good job of explaining all this, I'd appreciate it very much. I expect that I wouldn't be the only one. What's something you'd recommend to a junior developer so that they wouldn't be one of the "many devs", as you put it, who do the wrong thing?
Re:I Avoid Async/Await by magnio on Saturday April 16, 2022 @12:49PM (#31053869)
Not the parent, but I recommend understanding the event loop first: here [0] is a very good talk. Then, read the chapter on promise on javascript.info [1], as it explains the problems Promise set out to solve (callback hell), then as usual the excellent MDN article [2].

[0] https://www.youtube.com/watch?v=8aGhZQkoFbQ

[1] https://javascript.info/async

[2] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guid...

Re:I Avoid Async/Await by garyrob on Sunday April 17, 2022 @12:37PM (#31062092)
Just watched that video. Thanks! Excellent resource. Moving on to your other links.
Re:I Avoid Async/Await by stareatgoats on Saturday April 16, 2022 @01:52PM (#31054410)
The whole async/await paradigm (including promises) is trying to fit square pegs into round holes (trying to make async processes look like they are synchronous). Just look at the comments on this page; even though async/await/promises have been around for years they are still causing difficulties for even the most experienced devs.

It doesn't mean it can't be mastered, it can, but the whole paradigm is so fraught with pitfalls and conceptual difficulties that an codebase that uses it in any extensive way will forever be unstable. The whole thing is supposed to help against callback hell, but that can be better solved by a simple thenable object.

Not a popular opinion I know, but there you go.

Re:I Avoid Async/Await by eurasiantiger on Saturday April 16, 2022 @03:16PM (#31055203)
> trying to make async processes look like they are synchronous

That is NOT what async/await is about — after all, async explicitly marks functions as asynchronous. Not exactly trying to hide that.

Async/await is syntactic sugar. It is there to make working with Promises less verbose.

That is almost all: await pauses current function execution in the main event loop, which is an important detail

Re:I Avoid Async/Await by doliveira on Saturday April 16, 2022 @03:05PM (#31055122)
> simple thenable object

Wouldn't you simply be reinventing promises?

Re:I Avoid Async/Await by stareatgoats on Sunday April 17, 2022 @03:59AM (#31059317)
Actually no. A promise is a thenable oject, true, but a complex one. Simple thenable objects are in the simplest form objects that internally just holds an array of methods defined in each then() block. These methods then execute one after another at runtime. Any slightly experienced dev can probably create a library object or class like this in under 100 loc as a dropin replacement for most promises needs, and get a firm grasp of the internals in addition.

The usage of such an object won't be as terse and seemingly elegant as await syntax, but this is part of the problem: with await/promises so much of the complex logic is hidden from view and instead needs to reside the head of each dev, where it needs to compete with a thousand other things that need attention. It's an expression of the constant but unhealthy tendency towards golfing that pervades our field IMO.

Re:I Avoid Async/Await by doliveira on Sunday April 17, 2022 @11:56AM (#31061742)
Yeah, up until you need some extra functionality and you fall into the Inner-Platform effect (https://en.m.wikipedia.org/wiki/Inner-platform_effect). Promises are a well-established and battle-tested standard, I'd be very weary of someone reimplementing them for no reason.
Re:I Avoid Async/Await by lmc on Saturday April 16, 2022 @12:41PM (#31053791)
This doesn't directly answer your question, but... something to watch out for is experienced developers can also struggle with promises and async code if they've spent most of their career working with sync code. And when we 'get' it, the difficulty of the journey is often understated. This stuff can be hard, so don't sweat it if it seems frustrating. (On the other hand, it may be easier if you don't have years of sync patterns to mentally set aside )
Re:I Avoid Async/Await by garyrob on Sunday April 17, 2022 @12:38PM (#31062103)
Thanks for this feedback.
Re:I Avoid Async/Await by eurasiantiger on Sunday April 17, 2022 @03:26AM (#31059163)
Nobody has yet noticed that it’s only wrapped in TWO Promises. I take it nobody really understands the syntax, author included.
Re:I Avoid Async/Await by savanaly on Saturday April 16, 2022 @11:32AM (#31053101)
This is absolutely correct why is it being downvoted lol
Re:I Avoid Async/Await by Kwantuum on Saturday April 16, 2022 @12:23PM (#31053606)
It really isn't, most native/standard APIs do not return Promises. Lots of things use callbacks. Being comfortable creating Promises from a callback-based API is definitely something any competent JS dev ought to be able to do.
Re:I Avoid Async/Await by MrJohz on Saturday April 16, 2022 @04:20PM (#31055755)
A lot of APIs produce promises these days. The big one that I always need to promisify is `setTimeout`, but apart from that, I tend to find that if I'm using the `new Promise` API, I'm usually doing something wrong.

With Node APIs, there's a promisified version of pretty much everything. With browser APIs, there's usually a version with promises, and I'm struggling to think of an asynchronous API without promises that hasn't been superseded by something else (e.g. XMLHttpRequest -> fetch). If I'm converting from an event-emitter API to promises, there's usually going to be an impedance mismatch between the expectations of the event-based system and the promise-based system, and I probably need to explore another option.

I agree that any competent JS dev should understand how to create promises "from scratch" like this. But it still should probably be a fairly rare occurrence, and if I see a lot of `new Promise` calls in one place, it's pretty much always a sign that someone doesn't really understand how promises work.

Re:I Avoid Async/Await by bin_bash on Saturday April 16, 2022 @08:05AM (#31051650)
I prefer async/await but this is a contrived example.

For starters you can just write:

    const myfunc = () => new Promise(resolve => resolve())
or

    Promise.resolve()
Re:I Avoid Async/Await by Aeolun on Saturday April 16, 2022 @08:53AM (#31051917)

   const myfunc = () => new Promise(resolve => resolve())
Is there anyone that actually likes this structure? I find it really hard to reason about what a line like this does. What is the upper limit on double arrows in one line?
Re:I Avoid Async/Await by bin_bash on Saturday April 16, 2022 @09:00AM (#31051960)
here's a real world example I use all the time:

    const wait = (ms = 500) => new Promise(resolve => setTimeout(resolve, ms));
then:

    async function () {
      await wait(1000);
    }
Perhaps it's hard to grok but it sure is a handy one-liner.

Of course in newer versions of node we can now do:

    import {
      setTimeout,
    } from 'timers/promises';

    await setTimeout(100);
Re:I Avoid Async/Await by joe_guy on Saturday April 16, 2022 @11:44AM (#31053218)
There's no merit of what should be a project wide function being a single line instead of 3 to avoid "hard to grok" syntax.
Re:I Avoid Async/Await by deschutes on Saturday April 16, 2022 @12:28PM (#31053659)
Wait till you see the rest of javascript...
Re:I Avoid Async/Await by eurasiantiger on Saturday April 16, 2022 @05:36PM (#31056304)
Your comment is snarky and categorically dismissive. Care to explain why you have taken such an aggressive stance against JS?
Re:I Avoid Async/Await by love2read on Saturday April 16, 2022 @09:57AM (#31052363)
this works in older versions of node too:

const wait = require(‘util’).promisify(setTimeout)

Re:I Avoid Async/Await by oauea on Saturday April 16, 2022 @11:37AM (#31053144)
but not in the browser
Re:I Avoid Async/Await by eurasiantiger on Saturday April 16, 2022 @09:39AM (#31052223)
It’s the cleanest way to wrap a event-emitting library, for example. async/await is not applicable in such a case where resolve needs to be explicitly called in a callback.

    async function process() {
      return new Promise((resolve, reject) => {
        emitter.on(”error”, (err) => reject(err));
        emitter.on(”end”, (result) => resolve(result));
        emitter.start();
      });
    }
async is not strictly needed here, but I find it a good practice to use it on functions which immediately return a Promise anyway.
Re:I Avoid Async/Await by zkldi on Saturday April 16, 2022 @10:31AM (#31052629)
I don't think it's a good practice, atleast if you have typescript jn your stack. It's redundant and wraps the promise in another promise.
Re:I Avoid Async/Await by eurasiantiger on Saturday April 16, 2022 @11:14AM (#31052947)
IIRC that doesn’t actually happen if a native Promise is returned.
Re:I Avoid Async/Await by eyelidlessness on Saturday April 16, 2022 @12:33PM (#31053727)
It does, but only if you also add a redundant await. Which I still do, even in TypeScript, unless there’s a compelling performance reason not to. I disagree with the article overall, but I do agree that making asynchrony as explicit as possible is a good idea. Otherwise you end up with code like:

  async function foo(bar) {
    // ...
  }

  function nonObviousAsyncFn() {
    // ...
    return foo(quux);
  }
Explicit return types would help, but most people don’t write them unless they’re forced by a linter.
Re:I Avoid Async/Await by Aeolun on Sunday April 17, 2022 @06:13AM (#31059806)
This would still return a promise for both functions. My IDE at least is really obvious about what happens.
Re:I Avoid Async/Await by eurasiantiger on Sunday April 17, 2022 @03:24AM (#31059155)
Yes, this is a common anti-pattern

    return await foo();
Re:I Avoid Async/Await by albedoa on Saturday April 16, 2022 @01:20PM (#31054138)
Your gripe is that there are two arrows on one line? The second is an argument to the promise constructor. You should be able to reason about this line easily.
Re:I Avoid Async/Await by Aeolun on Sunday April 17, 2022 @06:11AM (#31059790)
I think I was unclear. Of course you can figure out what the line does. It’s just that you have to figure it out.
Re:I Avoid Async/Await by albedoa on Sunday April 17, 2022 @10:31AM (#31061087)
If the implication is not that the "double arrows" are tripping you up, then yes you were unclear.
Re:I Avoid Async/Await by wildrhythms on Saturday April 16, 2022 @08:28AM (#31051766)
As soon as you need to chain Promises, this gets very annoying and you quickly find yourself in a nested callback hell.
Re:I Avoid Async/Await by bin_bash on Saturday April 16, 2022 @08:53AM (#31051919)
If you’re nesting “new Promise” you’re almost certainly doing it wrong. This is for generating new promises not executing them.
Re:I Avoid Async/Await by kabes on Saturday April 16, 2022 @12:05PM (#31053399)
I agree but second one could just be `() => Promise.resolve()`
Re:I Avoid Async/Await by adamauckland on Saturday April 16, 2022 @12:24PM (#31053618)
It could, but depending on how you use it, you're changing scoping.
Re:I Avoid Async/Await by kabes on Saturday April 16, 2022 @05:42PM (#31056347)
True. But these days it's rare to see a scope other than what you'd expect from the short hand version. Old school JS with plain prototypes and flexible 'this' scope is very powerful, but is too hard to understand and thus rare these days.
Re:I Avoid Async/Await by eurasiantiger on Sunday April 17, 2022 @11:08AM (#31061365)
That’s one of the neat things about JS: even outside the prototypal inheritance chain, classes can be (partially) composed of generalized, reusable functions, a bit like traits. Just import a function and bind it in the constructor (call).
Re:I Avoid Async/Await by phendrenad2 on Saturday April 16, 2022 @02:39PM (#31054880)
> First off, async/await is promises. It's merely syntactic sugar. The point of async/await is not to never have the word "Promise" appear in your code

Sounds like you didn't understand the point of the article. Unfortunately, you're arguing against something the author never said, the author never said that async/await weren't promises. You just got stuck on the fact that the author used the term "promise" for the non-async/await style of code. I understood what the author meant, anyway.

> "People can mess this up" was never an argument

Unfortunately, you're arguing against something the author never said here, also. The author is saying that non-async/await code is better code than async/await. Not that "people can mess this up" or something.

Re:I Avoid Async/Await by chmod775 on Saturday April 16, 2022 @03:31PM (#31055356)
> Sounds like you didn't understand the point of the article. Unfortunately, you're arguing against something the author never said, the author never said that async/await weren't promises.

Well. I never said that I didn't understand the article. Unfortunately you are arguing against something I never said. Funny how that works.

> The author is saying that non-async/await code is better code than async/await.

And what does "better" mean to the author?

The entire first section is devoted to the author talking about criteria such as "brittle"-ness, "error-prone"-ness, and "footguns".

You seem to be getting stuck up on the fact that the author never used the exact same wording as I did.

Re:I Avoid Async/Await by mananaysiempre on Saturday April 16, 2022 @03:26PM (#31055302)
> The point of async/await is not to never have the word "Promise" appear in your code.

Indeed it is not the point, and in that sense your criticism of the article is correct.

However, there are people who argue[1] that it is, in fact, a good thing to never have the word "Promise" appear in your code, that not having the freedom to execute whatever code you want after launching an asynchronous operation and instead having the predictability of always returning to the scheduler makes reasoning about asynchronous code much simpler. (For one thing, you can now reliably think—and have syntactic support for thinking—in terms of sequential coroutines with predictable yield points and known parents.)

In the context that Smith refers to (trio vs asyncio in Python, regarding which also see the first, more Python-specific but IMO better-argued post[2]) my experience actually bears that out. How well this works given the historical API baggage in JavaScript, I don’t know, but probably not very well.

Another curious thing is that the line of research JavaScript promises originate from[3] (Mark Miller co-wrote the spec proposal IIRC) does not use them as the user interface; instead, using the "eventual send"[4], you queue up method calls to objects (which can be promised results of other queued calls, and you may pass other promised objects as arguments).

I cannot clearly articulate the relation of this to Smith’s notion of “structured concurrency” (I wish I could), but in any case it contributes some more weight to the opinion that the ergonomics of Promises are indeed subpar. It might well be, though, that in the context of JavaScript you can’t actually enforce enough structure on the preexisting APIs to build a superior alternative.

[1] https://vorpus.org/blog/notes-on-structured-concurrency-or-g...

[2] https://vorpus.org/blog/some-thoughts-on-asynchronous-api-de...

[3] http://erights.org/talks/thesis/index.html

[4] http://erights.org/elib/distrib/pipeline.html

Re:I Avoid Async/Await by whatdidiexpect2 on Saturday April 16, 2022 @12:32PM (#31053706)
It’s just another willfully ignorant post by a mediocre JavaScript developer. Are you shocked?
Re:I Avoid Async/Await by BenoitEssiambre on Saturday April 16, 2022 @08:27AM (#31051762)
Async/await is certainly not promises. In fact it would be much better implemented without promises as I proposed here: https://es.discourse.group/t/callback-based-simplified-async...

I would even say that async/await is anti-promise, it takes the main functionality of promises, a caching layer for results and errors that allows you to add the code continuation later and elsewhere (which is a major footgun imo) and coerces the execution flow back to going on the next line and provided immediately at compile time which results in a cleaner flow but not as clean, stateless, efficient or functional as if you were to remove the promises completely. Having an additional caching layer and state machine around every asynchronous function call is quite inefficient.

The essence of async/await is not promises, it's the underlying javascript generator (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...) functionality combined with asynchronous code to stop and start the generator. It's the ability to pause and resume function execution based on asynchronous operations.

The promise functionality, the caching layer and state machine for results is basically sanitized away with async/await, it becomes dead-weight computation. The only benefit of promises in async/await code is being able to more easily interface with other promise laden code which you don't need once you have async/await and a library like https://www.npmjs.com/package/async for more complex cases.

Note that promises based async/await is also a mess of an implementation that breaks stack traces and needs to support tons of odd statement corner cases (basically anything that can return an object that could be a promise) whereas a continuation passing style async/await would be a much simpler implementation that would only apply to function calls and maintain stack traces. We would get that stack trace support automatically because of the great work of whoever implemented javascript generators which seem to already carry stack traces across paused/resumed functions (if you don't wrap in promises).

Re:I Avoid Async/Await by nameisname on Saturday April 16, 2022 @09:02AM (#31051975)
I don't agree with you fully. Async most certainly always returns a promise and as such can easily be combined with promise functions, like Promise.all. Your understanding of stack traces on promises is also old/uninformed. I work on an enterprise grade node app that was started back in node 0.11. Bluebird, which was what one used, offers good stack tracing and improved stack tracing has been available for async await for a few years now. Infact, I don't think I have had to go out of my way to get a decent stack trace in about a year or more now. There are certain paradigms you can adopt that keeps promise chains clean just like you would adopt with callbacks. I can't comment on if async await was implemented poorly as you are certainly more knowledgeable there with the RFC you shared.
Re:I Avoid Async/Await by gorjusborg on Saturday April 16, 2022 @09:25AM (#31052131)
Exactly. Async/await just an alternative way to represent a promise chain.

My feeling is that mixing the use of promise chains and async/await can make code hard to follow, so I ask colleages to not mix them in the same function.

Sure, async/await is syntactic sugar for a generator that chains promises into a promise chain for you. Not sure why you'd care. That is an implementation detail that never gets exposed to the user.

The only time I feel that promise chains are better async/await is when treating Promise as a monad https://blog.bitsrc.io/out-with-async-await-and-in-with-prom...