Hacker News new | ask | show | jobs
by ryandvm 3700 days ago
The saving grace of JavaScript's everything-is-async, single threaded model was that it was just slightly less difficult to reason about than multiple threads and shared state. (Though I'd say that's debatable...)

My guess is that, despite the sugar coating that JavaScript's async internals have received of late, writing stable multi-threaded code with JavaScript is going to be hard.

JavaScript now has the safety of multi-threaded code with the ease of asynchronicity!

2 comments

SharedArrayBuffer only allows plain typed (byte) arrays to be shared at least. Arbitrary javascript objects can't be shared, so there's a very clear division about what can get affected by other threads and what can't. You don't have to worry about whether existing libraries are thread-safe, etc.
Not only that, but if you don't need "Shared" array buffers (meaning more than one thread using it at once) you can use "Transferrable" [1] ArrayBuffers.

It's just a zero-copy transfer to a worker (or from a worker) but it makes sure the "sender" doesn't have access to the memory any more.

It's incredibly easy to use, avoids all the common issues and pitfalls with shared memory, and being zero-copy it's stupidly fast.

Obviously it's not a replacement for true shared memory, but i've used it in the past to do some image processing in the browser (broke the image into chunks, and transferred each chunk to a worker to process, then return and stitch them all back together).

[1]https://developer.mozilla.org/en-US/docs/Web/API/Transferabl...

That's pretty rad - the vast majority of what I would see myself wanting to do in a multithreaded javascript world would be limited to something with transferrable arraybuffers. Like: "hey, worker, go do some work and lmk when you're done". Moving big chunks of memory around in ways that atomically only ever have one allowed accessing thread would be plenty.
I thought this too, but the fact that you can't send only a chunk of an array buffer is a huge limitation. It basically limits you do using a background thread to do something, instead of dividing the work among many (well, you can, but you loose most of the benefit of transferrable).
Well you can still divide the work among many workers, you just need to incur the cost of copying/splitting the buffer before you start sending them off.

In most cases you know how many workers you want at the start of the program, so that cost of splitting/merging only happens once (and you can do that splitting/merging in a worker to avoid hanging the main thread) and then you can pass those chunks around freely.

For workloads like graphics, the cost of splitting/merging happens each time.
Until the point that someone makes the SharedJSON library to store JS objects in binary that is!
Why is everything-is-async so entrenched in JavaScript? I'd love to have pre-emption and blocking IO in JavaScript but it seems to have been thoroughly excluded by design. What is the reasoning behind this?
If you've ever used EventMachine in Ruby, or Twisted in Python, you've probably encountered the reason.

Blocking has simple semantics, but it's hard to scale. Everything-is-async is a little more complicated, but scales nicely. The problem is when you combine the two, you only get the worst of both approaches.

If most things are async, and you've only got one thread, and you block that thread, then your whole app is blocked. Now you've got the scaling problems that come with blocking, and the complexity that comes with async.

Well preemption and blocking should go hand in hand. The distinction of whether or not that's multi-threading is more of a semantic argument. I'd consider them the Amino acids that the multi-threading protein is made of.

The entirely async model means that if something accidentally endless loops, it kills everything. The only reason why we get the "This script is taking a long time" is because some preemption is provided as some magic beyond the scope of the JavaScript itself.

Unless you're using webworkers, then javascript runs in the UI thread. If you do blocking IO in the UI thread, then things freeze.
That's the problem in a nutshell, It needn't be that way.

Blocking IO is just one aspect. Anything that is not instantaneous is blocking. Without the ability to preempt something taking a long time you will always be prone to the user interface freezing.

Methods like Array.map() are specifically designed to manage bulk operations immediately, which, without preemption, is at odds with a design where the user interface should perform with the lowest latency.