Live data from Hacker News

Top-level await in JavaScript is a footgun

gist.github.com

41–50 of 115 posts

Re: Top-level await in JavaScript is a footgun

#41
post #24

I was never a fan of async/await in C#, I just never found it that useful. I've seen examples of it in JavaScript and the author is always like, "Look at what you normally have to write and now look at what you COULD write with async and await!" And it's like the same goddamn code with a few minor differences. If they axed the whole proposal, I could get through this holiday season without even being depressed a litt…

You could say this for literally every single feature that got implemented in a language. Eventually a language needs to change and get better and it does it through small incremental changes as ES is doing. Future developers will only use await/async and they will look at old syntax/semantics and have the same attitude towards it that you have now towards async/await.

Re: Top-level await in JavaScript is a footgun

#42
post #24

I was never a fan of async/await in C#, I just never found it that useful. I've seen examples of it in JavaScript and the author is always like, "Look at what you normally have to write and now look at what you COULD write with async and await!" And it's like the same goddamn code with a few minor differences. If they axed the whole proposal, I could get through this holiday season without even being depressed a litt…

I find async/await in C# to be super useful, and fairly addictive once you start using it. Say you want to do three independent things in a function. How would you run them in parallel without async? Delegates, background workers, or threads? With async, this is simply done with Task.WhenAll().

Agreed, there are situations where it's super useful and I never want to go back to the old way.

I did read up on the paper that explains how the compiler rewrites the code before being comfortable using await.

Re: Top-level await in JavaScript is a footgun

#43
post #10

Am I the only one who is happy using normal callbacks? I think they are the best representation of the asynchronous flow.

I'm also happy with normal callbacks, and have no plans on adopting promises or async/await. the flow control library "async" provides all the tools to keep code readable.

Re: Top-level await in JavaScript is a footgun

#44
post #38

Earlier quoted context omitted.

Agree that `await` by itself is useful, and your example shows the correct way to use it for concurrent activity. The problem with top-level await is that any modules depending on a module with a TLA cannot execute until it's done – the effects cascade and magnify throughout your app, rather than being confined to an explicitly async function.

It seems it would only cascade through the entire app if only the app was designed around dependencies being available first before other code is run. That is, if the app is designed badly. Again, I don't understand the point of the proposal in this context since nobody should be awaiting a long list of dependencies to load first. If you have 100 dependencies that means you have 100 HTTP requests, which is really a p…

> only the app was designed around dependencies being available first before other code is run

I'm not sure I follow – isn't that the definition of 'dependency'?

> If you have 100 dependencies that means you have 100 HTTP requests, which is really a poor design decision

Agree – and it turns out that's still the case with HTTP2. But bundling doesn't solve the problem of await blocking your app, unfortunately.

Re: Top-level await in JavaScript is a footgun

#45

Earlier quoted context omitted.

Whether or not it's a module or something else being awaited isn't the problem. The issue that if your app.js (or whatever) has dependencies on modules with top-level awaits, however far removed, it can't execute until they're done. In other words top-level await essentially blocks your entire app. Using await inside an explicitly async function is great, because it only blocks code inside that function – the effects…

Ah, thank you, that makes sense. The term "footgun" is pretty interesting here, as it generally refers to something that allows one to shoot one's self in the foot. In this case, it's allows someone else to shoot you in the foot by hiding it in their code :/.

On the subject of whether or not this is really a problem because people can just be educated not to do this, the author said:

> You'll educate some of them, but not all. If you give people tools like with, eval, and top-level await, they will be misused, with bad consequences for users of the web.

This is still a footgun, because it makes it easy for people to introduce wide ranging and sometimes subtle bugs in their software.

Re: Top-level await in JavaScript is a footgun

#46

Earlier quoted context omitted.

Agree that `await` by itself is useful, and your example shows the correct way to use it for concurrent activity. The problem with top-level await is that any modules depending on a module with a TLA cannot execute until it's done – the effects cascade and magnify throughout your app, rather than being confined to an explicitly async function.

> The problem with top-level await is that any modules depending on a module with a TLA cannot execute until it's done – the effects cascade and magnify throughout your app, rather than being confined to an explicitly async function. Can't I already do this by putting a fs.readFileSync call or a synchronous AJAX call in my code?

The comparison to sync XHR is apt – put it this way, if you could go back in time and prevent synchronous XHR from being a thing, wouldn't you?

Re: Top-level await in JavaScript is a footgun

#47
post #12

Earlier quoted context omitted.

ES6 modules are declarative in the sense that the dependencies are statically analyzable.

True, but I interpreted the OP as criticising any execution of code on module load (other than declaring exports). Unless loading a file is a bottleneck, which I doubt.

> Unless loading a file is a bottleneck, which I doubt.

Loading a file is a bottleneck in the browser, right?

Re: Top-level await in JavaScript is a footgun

#48
post #38

Earlier quoted context omitted.

It seems it would only cascade through the entire app if only the app was designed around dependencies being available first before other code is run. That is, if the app is designed badly. Again, I don't understand the point of the proposal in this context since nobody should be awaiting a long list of dependencies to load first. If you have 100 dependencies that means you have 100 HTTP requests, which is really a p…

> only the app was designed around dependencies being available first before other code is run I'm not sure I follow – isn't that the definition of 'dependency'? > If you have 100 dependencies that means you have 100 HTTP requests, which is really a poor design decision Agree – and it turns out that's still the case with HTTP2. But bundling doesn't solve the problem of await blocking your app, unfortunately.

Some of the "other code" may not depend on the dependency. I think the word dependency may have been confusing in my statement.

Await doesn't actually block the entire app (or JS thread) like synchronous code does. It schedules the code that follows after an await in the same file or in an async function to run later. You probably already understand this, but my point is that other code outside of this context is free to continue running.

Re: Top-level await in JavaScript is a footgun

#49
Also, not to forget, `async import` would hide any potential syntax errors or errors in loading, since promises silence exceptions unless explicitly handled. Unless, of course, there's a special exception (no pun intended) to how promises work for imports.

Re: Top-level await in JavaScript is a footgun

#50
post #3

Earlier quoted context omitted.

It's not so much about blocking as it is about delaying the module waterfall. E.g. a module that did `await new Promise(_ => setTimeout(_, 5000))` at the top would prevent any of its dependents from being loaded for 5 seconds.

That's false. Module loading and execution are separate stages. The dependent modules would load before this await is ever reached.

Wait... so the OP's premise is completely wrong? Doesn't the body of a module have to complete before dependent modules will execute their bodies?

In other words, if you put

    for (let i = 0; i 
in the body of a module, wouldn't that block dependent modules for the duration of the loop?

Would top-level await be different?

edit The premise, as I understand it, is that the module itself is treated as the "currently-executing method" for the purpose of the `await`.

Post reply on HN