Earlier quoted context omitted.
It's nice, I guess, but huge? Instead of: async function main() { // code } main().catch(console.error); I'll be maybe writing: try { // code } catch (ex) { console.error(ex); } Hrm?
Top level await does more than remove a main function. If you import modules that use top level await, they will be resolved before the imports finish. To me this is most important in node where it's not uncommon to do async operations during initialization. Currently you either have to export a promise or an async function.
V8 adds support for top-level await
171–180 of 310 posts
Re: V8 adds support for top-level await
#172Earlier quoted context omitted.
No it's not. async/await's semantic is similar to coroutines, and is implemented by them.
Are you sure? From what I can find, async [0] causes a function to a return a promise, which then returns the result. Await [1] takes a promise, and waits for it to either be resolved or rejected. [0] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... [1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Re: V8 adds support for top-level await
#173Earlier quoted context omitted.
> Even at CPU level, "sequential" instructions aren't Within the context of the argument you are making, this is disingenuous. There's a big pile of transistors which determine whether it is safe to reorder those instructions.
Thar same pile of transistors, with a thick layer of software, determines whether it's safe to proceed a coroutine. const foo = async () { const ice = await freeze(water); const wCream = await whip(cream); const base = await pour(liquor, ice); const cocktail = await put(base, wCream); return cocktail.serve(); } In the above fragment, `freeze` and `whip` may run in any order or in parallel, you don't get to choose. St…
That example is:
const foo = async () =>
freeze(water)
.then(ice => whip(cream)
.then(wCream => pour(liquor, ice)
.then(base => put(base, cream)
.then(cocktail => cocktail.serve()))));Re: V8 adds support for top-level await
#174Earlier quoted context omitted.
> 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.
Threading implementation was async, not the programming model. Async programming style/model(as far as I'm aware) refers to the use of callbacks or coroutines, neither of which was common at all in C in the 1990s. Indeed, nginx caused big waves due to its superior IO performance as the first popular async http server released in 2004. But correct me if you have a different understanding of the term.
If I have a main program, and I fork a child to handle a task, and use shared memory to communicate, how is that any different than Javascript executing and and async call that sets or returns a value? The Javascript runtime has the same behavior as the OS scheduler in this, and if there's a single processor, it necessarily will only execute one instruction at a time. There's still plenty of pitfalls to worry about and that's why locks were (and are) useful, and why they are included with OS thread implementations (which are really just a nice API on forks and shared memory often dealt with by the OS for additional benefit).
Re: V8 adds support for top-level await
#175Earlier 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…
> when you use async/await instead of promises or callbacks, things do change because two sequential lines of code are no longer two truly sequential instructions I thought async/await was just syntactic sugar for promises?
Re: V8 adds support for top-level await
#176Earlier quoted context omitted.
How about an optimistic lock? async function deduct(amt) { var balance = await getBalance(); if (balance >= amt) await setBalance(balance - amt); var newbalance = await getBalance(); if (newbalance != (balance - amt)) { await setBalance(balance + amt); // tell the user the transaction failed... } }
This is getting off topic, but what would probably be best here is a test and set operation: setBalance(expectedBalance, newBalance)
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 now by trading in a developer’s comfort at near zero price.All this “await/promise has clear semantics and we think async” is pointless hope because code should never race with itself. Data should, and the storage must be there to kill associated problems once and forever.
Re: V8 adds support for top-level await
#177Earlier quoted context omitted.
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; }
async function totalSize(fol) {
const files = await fol.getFiles();
const sizes = await Promise.all(files.map(file => file.getSize()));
return sizes.reduce((acc, size) => acc + size);
}Re: V8 adds support for top-level await
#178Earlier quoted context omitted.
Const is a complete waste of time for non-primitive types. I defy anyone to show me a single bug in a popular program that could have been prevented by using const for a function local object. It can’t be done. There are bugs caused by mutatable state. There are bugs caused by reassigning globals. There has never been a bug caused by reassigning a function local variable while leaving it mutable.
Using the more restrictive construct until you actually need additional features (like identifier rebinding) is just engineering 101. I think the onus would be on you to prove that using a less restrictive concept is worthwhile because it saves you two keystrokes. That, in contrast, seems like the opposite of good engineering. Like using classes over structs because class is shorter to type. The more I think about yo…
On the other hand, there are some good reasons not to use use const everywhere we can. Paul Sweeney lists a few here: https://medium.com/@PepsRyuu/use-let-by-default-not-const-58...
I'm not sure where I land yet. Perhaps it's a decision to make separately for each codebase.
Re: V8 adds support for top-level await
#179Earlier quoted context omitted.
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?
I mean, if something like this is behind a promise or an async call, that probably means that there's I/O involved (no point using async to access local in-memory data), which means that the local locking primitive won't be incredibly useful, you're going to have to lock it via whatever mechanism the I/O channel (or some API using the I/O channel) provides, such as file locking or some API call or something.
That said, if new developers just blindly use async/await without understanding what's going on ... well that's another problem.
The field is filled with foot-guns, for some definition of guns (and for some definition of foot).
The problem with C, or better the problem with pre-emptive multitasking in general, is that even relatively knowledgeable developers were constantly hitting subtle issues with the memory model. Consider the good old trap of efficiently lazy-initializing a Singleton in Java: https://en.m.wikipedia.org/wiki/Double-checked_locking#Usage...
Re: V8 adds support for top-level await
#180Earlier quoted context omitted.
So use a Promise like a lock then. If it exists, wait on it. If not create one… Of course you don’t need to use the existence of a Promise as your Boolean in this case. You can simply use the Boolean state in addition to the Promise because your code is not going to yield while atomically setting one Boolean variable.
That would unblock all coroutines waiting for the promise, instead of just the first one. It's not that trivial, that's the point.
let lock = Promise.resolve()
const wait = (callback) => lock = lock.then(() => callback());
Until the callback resolves the lock is held, so you can do: wait(async () => {
await step1();
await step2();
// release the lock.
});