Live data from Hacker News

V8 adds support for top-level await

chromium.googlesource.com

271–280 of 310 posts

Re: V8 adds support for top-level await

#271

All this async code without decent locking primitives is leading to a rabbit hole of race conditions... It doesn't matter that it's all single threaded if all your function calls may or may not block and run a bunch of other code in the meantime, mutating all kinds of state. I feel like JavaScript developers of the 2020's are going to relearn the same things the C programmers of the 1990's learn, just a few levels of…

I am not sure how the parent comment has received so many upvotes. OP has some fundamental misunderstanding of mutexes and the purpose of async io.

Locking primitives are completely unnecessary in any single threaded program. Also mutexes in C or otherwise are way older than 1990. They go back to the 1950s.

When your program is single threaded, a simple boolean flag variable can act as a mutex, you don't need a mutex primitive for this. Mutexes are for situations where flag variables can change state between your instructions to check and set a flag. This can only happen in multithreaded or interrupt driven code.

Infact, the entire purpose of async programming is to stop the use of mutexes and multiple threads to perform concurrent io, which is something that can be performed by a single thread. This is the foundational premise of nodejs.

Re: V8 adds support for top-level await

#272

So COMEFROM is now a first-class feature of the most popular programming language in the world. Intercal really was ahead of its time.

I'm not sure I quite see the analogy to COMEFROM.

The "problem" with COMEFROM is that at the "target" location (the one control comes from) there is no in-source indication of the control flow transfer. So what looks like linear code turns out to have this unexpected detour to the location of the COMEFROM instruction. This hinders understandability of the code.

"await" in JS doesn't have that problem: there is an explicit control flow operation, in the form of resolving a promise, that eventually transfers control to the location of the "await" call. And even then, it's async from a run-to-completion perspective, so doesn't affect linear code, modulo other await calls. I guess the concern is that you could have linear code that calls a function, which does an "await" and you would effectively have a control flow detour that's hidden from view? In that sense, I guess this is sort of like COMEFROM... At least you have to explicitly opt in (via "async function", or async module) for it to be a problem.

Anyway, a better analogy from my point of view is that async/await is a very limited form of call-with-current-continuation or so. And with generator functions, JS already had that sort of, but without some of the nice ergonomics.

Re: V8 adds support for top-level await

#273

Earlier quoted context omitted.

Javascript devs, for all their flaws, understand async programming far, far better than the average C programmer. Indeed, your assertion that we need "locking primitives" to counteract "race conditions" is evidence of that. Yes, JavaScript can have race conditions, but not multi-threaded race conditions that cause resource contention [0]. So what good would locking primitives be? And as a solution to single-threaded…

Here's an example of how you might get a race condition in JS: async function deduct(amt) { var balance = await getBalance(); if (balance >= amt) return await setBalance(balance - amt); } One way to resolve this would be with a mutex to protect the balance during the critical section (which is async). What would you suggest instead?

Use the datastore... use atomic updates and transactions update balance, where balance >= amount, set balance = x

Re: V8 adds support for top-level await

#276
post #201

Earlier quoted context omitted.

If the callback throws an error, none of the awaiting coroutines will run, and adding a catch would break the callback error propagation.

Yes, but he was giving the simplest implementation for the sake of brevity. Also, nobody said that we need callback error propagation in a priority queue to act the same exact way that callback error propagation works in a straight promise chain... but if you just want to reject all waiters in case of an error, you can do that. Take a look at sindresorhus' p-queue project - https://github.com/sindresorhus/p-queue - w…

Even the original solution is a lot more complex than just checking some primitive value. A lock is still needed when doing concurrency on a single thread, which is the point I'm making anyway.

> Long story short: Your problem is with ordering, not locking.

Look up the definition of a concurrency lock.

Re: V8 adds support for top-level await

#277
post #203

Earlier quoted context omitted.

No, I didn't. That's how a lock works. Only one coroutine (or thread) is allowed to hold it at a time while the others must await their turn. A state machine is a lot more than a boolean variable.

Both of your statements are incorrect. Locks are an abstract concept that have many different behaviors. In a multi-threaded world (not JS) a certain type of synchronization lock, such as a single-writer/multiple-reader locking mechanism would specifically allow you to unblock all waiting coroutines. Another type of lock might only allow the first waiter to unblock. There simply is no concurrency in JS though, so the…

First look up the definition of a lock and mutual exclusion. There are two types of basic locks: mutexes and semaphores. Both keep two threads or coroutines from executing a block of code/instructions at the same time, and both work in a similar way I already described.

> There simply is no concurrency in JS though,

There is no parallelism in JS (well there is with workers now), concurrency is not parallelism.

Re: V8 adds support for top-level await

#278

Earlier quoted context omitted.

Javascript devs, for all their flaws, understand async programming far, far better than the average C programmer. Indeed, your assertion that we need "locking primitives" to counteract "race conditions" is evidence of that. Yes, JavaScript can have race conditions, but not multi-threaded race conditions that cause resource contention [0]. So what good would locking primitives be? And as a solution to single-threaded…

> 1990s C was decidedly synchronous You know that Javascript is a C(++) program? That when you use TCP, the protocol is in C? The ethernet driver is written in C? The OS scheduler is written in C? That you're programming in a little sandbox, and all the concurrency around you in managed in C? There has never been anything synchronous about C.

> You know that Javascript is a C(++) program?

JavaScript is not implementation-defined. There's a spec, and there are interpreters in a number of different languages. Sure, most common JS interpreters/JIT and otherwise, are in C++, but that says nothing about whether or not they use an event-driven style underneath to program the interpreter.

And regarding C being synchronous, I'm talking about programming models, not underlying architecture of the computer or OS. If everything is programmed in async as you seem to suggest here, then we do we bother distinguishing the two? Why do most books on networking programming have a separate chapter devoted to async or event-driven programming? Why does the Unix socket API have `socket.setblocking(0)`?

But I suspect you know damn well what I'm talking about.

I've got to get off of social media.

Re: V8 adds support for top-level await

#279
post #263
post #197

Earlier quoted context omitted.

I don't think I'd call any of those reasons good; the main thrust of them is "const doesn't do everything, so don't let it do anything". If that line of reasoning is appealing, then one might as well continue using var.

The main benefit of let/const over var is that it's block scoped. The benefit of const over let is pretty much nothing, much. It only prevents some limited form of re-binding. You can still easily re-bind const variables from inside a function: const x = 1; (function() { const x = 2; console.log(x); })() or from an argument: const x = 1; (function(x) { console.log(x); })(2) So it has limited value in any code that us…

Shadowing isn't rebinding?

If you've declared const x = 1, then that will hold for your scope?

If you go into a separate scope... Well, then you're in a separate scope?

Post reply on HN