Live data from Hacker News

V8 adds support for top-level await

chromium.googlesource.com

231–240 of 310 posts

Re: V8 adds support for top-level await

#231
post #53

I think async/await probably makes more sense in a typed language, where a compiler can tell you when you're missing an await, or at least warn you about not dealing with potential side effects and error handling. For something like JavaScript, it'd make more sense to me to have the runtime always and implicitly await the result of async functions, and instead make developers explicitly say when they wish for the res…

I like your idea, but I don't see how it could work in an untyped language. Consider: function foo() { return 1; } function bar() { return fetch('http://example.com'); } // implicitly async function qux() { const fn = Math.random() > 1/2 ? foo : bar; fn(); return 1; } Is qux() synchronous?

The point isn't so much whether qux() is async or not, it's whether the caller wants it to execute before subsequent statements or not. In the current state of the world:

    const a = await qux()
    const b = qux()
    const c = await qux()
    const d = (await b) + 5
But if I had my little way:

    const a = qux()
    const b = async qux()
    const c = qux()
    const d = b + 5
The weird one is `d` of course, why would it implicitly await there? Because `+` tries to get the value of `b` in order to add `5`.

How the runtime would optimize this scenario I don't know, it could probably statically determine that qux() may be async and therefore determine it would have to implicitly await its return value even if it's just the number. Obviously this is a contrived scenario, but I'd rather pay a small runtime cost, than pay the cognitive overhead of remembering to await things everywhere. Like most people, I forget things...

Re: V8 adds support for top-level await

#232
post #48

Earlier quoted context omitted.

Data races are impossible in JavaScript because it's single-threaded. Race conditions are possible in any language that can do anything asynchronous, which is basically all of them. But the general benefit you get from JS being single-threaded is the fact that any given callback is transactional. No other code will ever come in and mutate state between two regular lines of JavaScript code. Achieving this is pretty mu…

Since an async function is simply a function that returns a promise is there actually any difference between using async/await and using promises explicitly?

If you're compiling to ES2016 or lower, it will turn the promises into state machines.

Re: V8 adds support for top-level await

#233
post #15

Earlier quoted context omitted.

It's a stage 3 proposal[1], which according to the TC39 process[2] means "the solution is complete and no further work is possible without implementation experience, significant usage and external feedback." In other words, it's all but standardized. Barring significant blockers coming from actual implementation experience this will most likely be ratified. [1]: https://github.com/tc39/proposal-top-level-await [2]: h…

And the way the TC39 works, it won't progress until multiple vendors can implement it. Being implemented by multiple js engines is how JS features become standardized.

Yup, thanks for adding that. It's a good process I think, though it is kind of a double edged sword. On the one hand it's nice because it means the standard isn't bloated with a bunch of stuff that no one implements, but on the other hand it also means it's much harder to get rid of stuff that turns out to maybe not be such a great idea after all, leading to bloat anyway.

Still, it's a pretty good process I think.

Re: V8 adds support for top-level await

#234
post #176

Earlier quoted context omitted.

This is getting off topic, but what would probably be best here is a test and set operation: setBalance(expectedBalance, newBalance)

And they call average C developer less experienced in async. The only correct ‘update balance’ operation is: INSERT INTO CashFlow (date, income, expense) VALUES (:date, :income, 0) Get balance: SELECT sum(income)-sum(expense) AS balance FROM CashFlow What modern js still has to reinvent is in-client synchronizing storage which naturally resolves who waits on what (if at all). This is partially simulated by reactjs no…

Operational transformations are not the "only correct" way to update an integer and it comes with a number of performance and memory/storage consumption implications.

>What modern js still has to reinvent is in-client synchronizing storage which naturally resolves who waits on what (if at all)

I don't understand what this means but it makes me curious. What would be an example of this in one of the languages that have already "reinvented" it?

Re: V8 adds support for top-level await

#236
post #66

Earlier quoted context omitted.

No magic is rich — they swallow errors, for one. In any case, I don't think anything I said precludes the creation of promises outside of async functions, in fact quite the opposite. The difference is that the runtime would implicitly await the resolution of a promise returned from a function (any function, there'd be no such thing as an "async function") and if you actually wanted things to progress asyncronously yo…

> and the only way to know if you need await is to peruse docs (hoping that they're accurate) or judiciously sprinkle it everywhere. Knowing whether what you’re calling is async is part of knowing what you’re calling at all. Sprinkling await everywhere to try to mask the difference is horrifying. (I kind of wish it didn’t work on non-thenables for that reason – half the time, it’s a bug.)

But it can change under your feet – someone might change a function to be async, that used to be sync, and now your code is broken. The code does the same thing, it's just that it turned from returning a value to returning the promise of a value, and now your code is broken. Maybe it's not the function you're calling, but a function further down the stack, that you don't even know about.

It may be that the docs are bad and don't even tell you it returns a promise. Heck, maybe it only returns a promise on Tuesdays, or at random like some other commenter wrote – you'd have to then sprinke `await` there to make sure you're ok, even if most of the time you don't need it.

Re: V8 adds support for top-level await

#237

Earlier quoted context omitted.

I like your idea, but I don't see how it could work in an untyped language. Consider: function foo() { return 1; } function bar() { return fetch('http://example.com'); } // implicitly async function qux() { const fn = Math.random() > 1/2 ? foo : bar; fn(); return 1; } Is qux() synchronous?

The function is both synchronous and asynchronous until it is called by the caller. This is called Shannon's Cat. Jokes aside, I'd like to add that just because a function returns a promise doesn't mean the caller will always want to wait for it to resolve. I think of `await` as a simple modifier that casts the return value from a `Promise ` into `T`. By getting rid of the modifier, we can no longer assume that the c…

You're right of course, and I have zero data to back this up other than anecdotes from my own experience, but still I posit the most common case is that the caller wants to await the return value, not run the function asynchronously. This is why I think it'd make more sense to flip the semantics so you'd have implicit await, and have to explicitly mark the things you want to run asynchronously.

Re: V8 adds support for top-level await

#238
post #219

Earlier quoted context omitted.

In that case, the code would be sync and there wouldn't be a "race condition".

I just the other day had to step in to deal with a race condition in a frontend caused by improperly handling async API requests. It's dangerous to assume that just because JS has a single main thread that you don't need to think about sequencing of operations and locking.

While writing async code can indeed be challenging, I just disagree with the idea that JavaScript is lacking good "locking primitives":

> All this async code without decent locking primitives is leading to a rabbit hole of race conditions...

No, it's not. Here's a lock:

   let lock = Promise.resolve()
   // ...
   lock = lock.then(() => { /* critical section */ })
For the rare instances where more complex async flow control is required, there are a bunch of libraries for that (e.g. async[0], p-queue[1], etc.).

[0] https://caolan.github.io/async/v3/docs.html

[1] https://github.com/sindresorhus/p-queue

Re: V8 adds support for top-level await

#239

Earlier quoted context omitted.

Locks are for concurrency. Parallelism is orthogonal. When you are forced to yield inside of your critical section (a database call, a file write, whatever), as is common in NodeJS, you must acquire a lock that another coroutine can't run through.

He got the terms wrong but he’s absolutely right. > There's no way for multiple branches of code to access the same variable at the same time in JS. This is 100% true and it’s the very reason why we do not need locks. There is no thing called a critical section in JavaScript because in JavaScript all of your code runs on a single thread and it’s all equal therefore none of it is more volatile than any other piece cod…

I don't think a critical section is necessarily related to parallelism, but concurrency (As shown in other examples in this thread). If you google for "critical section", the first link (Wikipedia) begins "In concurrent programming,".

Regardless, if you were to have need of a lock, which was the basis of this chain of comments unless I'm missremembering, a read-write lock could improve performance, regardless of things being singlel threaded, precisely because of things being async (If you have N async tasks all read-only locking an rwlock, they can go on to fire async requests, which will run concurrently, despite the execution being single threaded).

Re: V8 adds support for top-level await

#240
post #182

Earlier quoted context omitted.

Can anyone explain this to me, how is it a race condition?

Two concurrent calls to that code. Both of them get the same balance (100), pass the test and deduct the amount (75), getting to a -50 balance.

I correct myself. It leads to a 25 balance but possibly a double spend of those 50 dollars in two transactions.

The standard pattern is to use database row locking or calling a stored procedure that performs locking inside. Backend developers typically don't like the second solution but it provides a kind of API. Not to be overlooked if there are multiple services accessing it, especially in a polyglot environment.

Post reply on HN