Live data from Hacker News

V8 adds support for top-level await

chromium.googlesource.com

111–120 of 310 posts

Re: V8 adds support for top-level await

#111

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…

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…

> also ignorant, because 1990s C was decidedly synchronous

There was definitely threading in C in the 1990's, and threads are asynchronous by nature. There's a whole class of synchronization bugs that C and C++ developers had to learn to deal with, and while JavaScript developers get to avoid some by the nature of there being a single thread, that doesn't necessarily exempt them from all of them.

Re: V8 adds support for top-level await

#112

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?

>> What would you suggest instead?

On the front-end, I'd do a call to the server. On the server, I'd use a database transaction for that.

But I get your point. The way I deal with race condition in redux is to have a mutex. I.e. While something is being fetched, any call to this command is either ignored or queued up.

I use redux-saga for the "race-condition" and general flow, and call the various async functions from within sagas.

Re: V8 adds support for top-level await

#113
post #107

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…

Just because code is written in an asynchronous style doesn't prevent correctness errors that would not exist with locking. For instance, using everyone's favorite example, a bank: In no particular order: 1. Pizza company debits by balance by $5 2. I withdraw $5 Assuming both those operations can yield due to e.g. async requests and that they can be resumed at any point, I don't know which order they will yield or re…

Sure enough, but in javascript if your balance operations can yield due to async requests than you already have a bigger problem than a shared in-memory variable. Which is really the only case a standard os-lock can solve.

Let's say that balance is in a database or behind a REST api. An os-lock won't solve the problems of other processes trying to update that same resource.

Re: V8 adds support for top-level await

#114

Earlier quoted context omitted.

In javascript anywhere you see await, you've introduced an explicit scheduling point. There are cases where even in a single threaded env you want to "wait" until some other async process is complete. To use one of the old school examples: function transfer(amount, acct1, acct2) { var current_balance = await acc1.balance() if current_balance > amount { await acct1.sub(amount) await acct2.add(amount) } } Now what happ…

I don't think it really makes sense to essentially say "Javascript is rife with race conditions like any other language" just because yes, you still need to use transactions when using a database.

You're reading too much into the example. Your 'database' can be your frontend js state. There is nothing in the example that limits the problem to backend development.

Re: V8 adds support for top-level await

#115
post #70
post #65

Earlier quoted context omitted.

> two sequential lines of code are no longer two truly sequential instructions I've done a lot of async/await and I really can't think of any situations where this has been a concern for me. If you're mutating state in method calls without explicitly passing it around, that might be an issue but that's a deeper design issue IMHO.

One use case I often find is a caching layer between JS and a HTTP call. If there are 2 calls to a non cached endpoint, the HTTP will fire off twice before caching the result. You can work around this by returning the first promise from the cache, but this is essentially a mutex. Having locking primitives would solve this and require less boilerplate code.

Do you really want to block all of your http calls on the result of the prior call in the off-chance that they might share a result just so you can only cache the results and not the promises?

The reason we don't have a mutex in javascript is that there are better solutions to the problems it solves.

Re: V8 adds support for top-level await

#116
post #79
post #70

Earlier quoted context omitted.

One use case I often find is a caching layer between JS and a HTTP call. If there are 2 calls to a non cached endpoint, the HTTP will fire off twice before caching the result. You can work around this by returning the first promise from the cache, but this is essentially a mutex. Having locking primitives would solve this and require less boilerplate code.

Seems like you should be able to build a lock mechanism fairly simply with async/await, but I agree, it would be nice if it was a built in primitive. It does feel a little silly to implement a lock function when the engine much be using one to support its async functionality in the first place, so you're introducing a lot of inefficiency.

I don't think it's true that the engine must be using a mutex to support it's async functionality. Or that it is true that an OS mutex will be any more efficient than alternative solutions.

A mutex really can only exist to serialize async code. If you want it serialized that likely means that code shouldn't be async in the first place.

Re: V8 adds support for top-level await

#117
post #107

Earlier quoted context omitted.

Just because code is written in an asynchronous style doesn't prevent correctness errors that would not exist with locking. For instance, using everyone's favorite example, a bank: In no particular order: 1. Pizza company debits by balance by $5 2. I withdraw $5 Assuming both those operations can yield due to e.g. async requests and that they can be resumed at any point, I don't know which order they will yield or re…

Sure enough, but in javascript if your balance operations can yield due to async requests than you already have a bigger problem than a shared in-memory variable. Which is really the only case a standard os-lock can solve. Let's say that balance is in a database or behind a REST api. An os-lock won't solve the problems of other processes trying to update that same resource.

You can imagine a function being unintentionally promoted into async-world for some kind of cross-cutting concern even if the data is stored in memory, e.g. if logging requires an async call, or if reporting metrics requires an async call.

While I agree that in the general practice in JavaScript programming stops you from this kind of footgun, you can still be caught off-guard if you end up thinking that this programming style is immune to this kind of problem: locking is a big hammer with its own problems, but it will never cause data incorrectness.

Re: V8 adds support for top-level await

#118
post #107

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…

Just because code is written in an asynchronous style doesn't prevent correctness errors that would not exist with locking. For instance, using everyone's favorite example, a bank: In no particular order: 1. Pizza company debits by balance by $5 2. I withdraw $5 Assuming both those operations can yield due to e.g. async requests and that they can be resumed at any point, I don't know which order they will yield or re…

> Just because code is written in an asynchronous style doesn't prevent correctness errors that would not exist with locking.

Yes, that's why I referenced passing around a reference to an immutable data structure, which is fairly common in JS.

Re: V8 adds support for top-level await

#119
post #107

Earlier quoted context omitted.

Just because code is written in an asynchronous style doesn't prevent correctness errors that would not exist with locking. For instance, using everyone's favorite example, a bank: In no particular order: 1. Pizza company debits by balance by $5 2. I withdraw $5 Assuming both those operations can yield due to e.g. async requests and that they can be resumed at any point, I don't know which order they will yield or re…

> Just because code is written in an asynchronous style doesn't prevent correctness errors that would not exist with locking. Yes, that's why I referenced passing around a reference to an immutable data structure, which is fairly common in JS.

How will you update the bank account with an immutable data structure? You need to mutate the balance somewhere.

Re: V8 adds support for top-level await

#120

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?

More subtly, here's an example of a hidden race condition in JS:

  async function totalSize(fol) {
    const files = await fol.getFiles();
    let totalSize = 0;
    await Promise.all(files.map(async file => {
      totalSize += await file.getSize();
    }));
    // totalSize is now way too small
    return totalSize;
  }
Post reply on HN