A big thing with Clojure is that it tries to provide a single, idiomatic way of doing things (though overuse of macros may cloud this picture at times). Generally, you can hop into any Clojure codebase and very quickly understand what's what.
Since JavaScript can do just about anything (functional, imperative, object-oriented, prototypical), there's no one "true" way of writing code, so while its possible use the "good parts" of JavaScript, everyone seems to have a different idea of what those are.
Flexibility can be a virtue, but I find myself preferring Clojure due to the constraints it provides.
That's arguably a lot of work, but the payoff is that you get to use your new hand tailored "language" anywhere js can run, which is not the case if you ignore js and just pick a language you like from the get go instead.
(editing to mention the fact that Clojure targets both servers (via JVM/CLR) and browsers (via ClojureScript) so you lose none of the reach you mention with JavaScript)
Clojure is built around the idea of immutability from ground up. It makes it natural to work with immutable data, and it guides you towards doing the right thing. An example of this would be transients, which allow you to do local mutation within a function, but the compiler will give you an error if you try to leak mutable state. All of this greatly reduces mental overhead for the developer because vast majority of code is referentially transparent. You can reason about functions in isolation without having to know anything about the rest of the application.
Meanwhile, simply bolting on immutable constructs on top of JavaScript puts the burden of using them correctly squarely on the developer. It's still perfectly possible to stick a mutable reference into your immutable record, and then pass it around and modify it. The language does nothing to help you there. Furthermore, you have to consider the culture and the ecosystem around the language. Pretty much all Js libraries are written in imperative style, and passing mutable data is the accepted practice.
And immutability is just one aspect, there are plenty of other differences that are very important for many daily tasks. One major difference is that Clojure is a much smaller and consistent language. Clojure code tends to follow a few accepted patterns that the community settled on. Js is a giant language that's grown organically and acquired tons of quirks and gotchas over the years. It's a minefield compared to Clojure.
Another day to day benefit of using Clojure is its tooling. You get reliable hot loading, which is pretty much impossible with Js, you have sane library management, minification, code pruning to function level, and code splitting out of the box. These features are especially important when shipping code across the network to the browser.
You also get a REPL where you can connect your editor to your running application, and interact with it directly from there. Any time you write a function, you're able to run it immediately and see what it does. There's no waiting for your code to compile, or the page to reload, or having to rebuild the state. It's an incredibly tight feedback loop.
In short, there's no comparison between ClojureScript and Js regardless of what constructs get bolted onto Js in the future.
If you are spending 100 ms calculating the new version of the immutable structure, you want to make sure that the nearby thread isn't making their own version. You don't want any of the two results silently overwritten. You want the nearby thread be sequential - to wait for you and base on your result.
eg
x = [1,2,3]
Thread1 -> x + [4] => [1,2,3,4]
Thread2 -> x + [5] => [1,2,3,5]
But you were expecting [1,2,3,4,5]
Reality was that you wanted an order to your events, normally enforced by locking, which the immutable vector doesn't seem to help you with; they were both able to update independently, but you actually wanted them to update dependently.
If you try to use immutable datastructures to avoid locking, then how is conflict resolution handled?
I think the answer would be that it doesn't help you avoid locking; either you lock & share a single reference to the latest version of your immutable vector, to enforce ordered events, or you define a resolution strategy separately. The immutability aspect just stops you from not having a resolution strategy -- which would always be incorrect
And if I understand correctly, the ideal scenario for immutable datastructures in concurrent scenarios is when you can define such a merge strategy (and safely give threads their own copy of the datastructure to muddle with, without actually having to copy the entire datastructure)
It does, if you believe serialization by locking is the main strategy to handle serialization (in which case, mutable or immutable, you still need to lock), and so... you still need locking. Serialization being the main scenario GP gave.
Your original answer didn't resolve the problem either -- fine, you didn't need to lock when adding elements to your immutable structure, but you still haven't reached serialization; you've just pushed the problem back another step.
The answer that I believe GP would need to correct his understanding, (and much more importantly, the answer that I'm interested in :-) is what serialization strategies does immutable datastructures enable, if not locking?
The other correction GP seems to require is whether serialization is actually that important in general, and whether functional programmers tend to experience otherwise... But I don't care about that answer :-)
Depending on your performance goals, Compare-and-Set with retries a la clojure's atom reference construct.
Regards your other question(s), I will just add that I answered many similar questions for myself (as well as disabusing myself of a lot of misconceptions) by undertaking to get a basic understanding of Haskell.
If you're planning to have multiple threads append items to a list, the immutable way to do it is to have each thread return a list of items to append to the main list, then fold those items into the list.
Again using git as an example, there is the persistent data structure, aka the commit graph, as well as mutable references to commits, aka branches. A change to what commit a branch references needs to be synchronized.
It's great to have git as a mental model in this discussion, really useful.
An easier example could be that you have a tree structure that represents a mathematical expression. The evaluation of every node could proceed on its own lightweight thread. The merge strategy would be to simply perform the appropriate operation on the results produced by the threads evaluating the child nodes.
You have customers' orders to buy items. One last item remains at your store. You accept one order and update HEAD. You accept another order in parallel and follow to merge. "Merge" here means that you need to return money to the customer and send out an apology e-mail.
More cumbersome than locking, isn't it? But possible, yes.
Your example is much better. I also was thinking of maintaining an account balance with a log (vs. synchronising updates to a stored amount), but it's not much different from your example.
And of course this "eventually consist" strategy is generally how things happen "at scale", persistent data structures or not.
Having used immutable data structures and concurrency in non-clojure languages, I mostly resort to something like concurrentML for concurrency. Message passing lets you solve situations like that in more elegant ways.
However, to agree with part of your point, an atom being updated with CAS can only ever progress linearly; if you bang 8 threads in a loop trying to update it, you might as well use a lock, because 7/8 of the work is going to be thrown out.
They should only be used for cases where on average one thread or less is working on updating it; anything more, and it needs to be split, the problem rethought, or some mechanism for not throwing out wasted work implemented.
It's not a silver bullet.