I object to the term "similar care". There's a big difference in what it takes to understand thread issues compared to single-threaded event-based systems.
For instance, if I'm not mistaken, your setTimeout thing is relying on precise timing. But this is documented when you look up setTimeout. It's not difficult to understand.
How to use locks and conditions/monitors correctly IS difficult to understand. You can't just read a few lines in the API documentation and proceed on to write correct code, beyond small toy examples.
setTimeout is obviously a simpler primitive to understand in isolation than monitors/conditionvars, but I would argue that when used in equally complex scenarios, similar challenges emerge.
Something like Dinning Philosophers is no less tricky to express + understand with an event loop, and with JS's async await probably would most cleanly be expressed in a style that mirrors a monitors/conditionvars version.
The fact that JS has added async await suggests demand for the convenience of concurrent blocking threads. While async-await manages to separate out non-async code to a degree, once an await happens, the global state can also be arbitrarily mutated no differently than with threads.
function block() { return new Promise(function(a,b) { setTimeout(a, 0); }); }
var x = 0;
(async function foo() {
x = 1;
await block();
console.log(x);
})().then();
(async function bar() {
x = 2;
})().then();
Concurrency is hard, but event loops aren't a magic wand.
For instance, if I'm not mistaken, your setTimeout thing is relying on precise timing. But this is documented when you look up setTimeout. It's not difficult to understand.
How to use locks and conditions/monitors correctly IS difficult to understand. You can't just read a few lines in the API documentation and proceed on to write correct code, beyond small toy examples.