Hacker News new | ask | show | jobs
by albertTJames 3398 days ago
If multithread is a possibility for the future of javascript why not make promises and async multithread and keep the same syntax we are using now... What is there to gain with workers ? It seems to me like an unnecessary addition ... but I am interested in the point of view of specialists on the matter. I may be wrong, but promises and async are for me a great formalism upon which one could build a future version of js that is multithreaded.
5 comments

Short answer, race conditions.

Here is a longer answer. Node currently uses cooperative multi-tasking. Each function call owns the CPU until it gives up the CPU by returning. Therefore all operations are implicitly atomic. Which makes them very easy to reason about.

As soon as you move to multithreading, NOTHING is atomic unless you lock it. You can even have problems with something as simple as:

    globalCounter = globalCounter + 1;
(If one thread is suspended between reading on the RHS and writing on the LHS, another thread can fetch fetch/write the value, and then that update gets lost when the first thread continues execution.)

There have been many cooperative async programming systems in the past. Every one that has moved to preemptive (which multi-threading is) has uncovered a lot of subtle, hard to spot, and hard to fix bugs because of losing implicit atomic guarantees.

So you go back to the safe solution of locking everything. But now locking/unlocking takes away a bunch of performance, limits parallelism, and creates the possibility for things like deadlocks. And now you might as well not bother with multiple CPUs! (See Python's GIL for a well-known example of this result.)

The challenge therefore is how to add some pre-emptive multitasking while avoiding creating too many unexpected nasty race conditions.

Here's my understanding of this:

Aysnc and threads are fundamentally different.

Aysnc refers to intelligently pausing/resuming many different operations. This is fantastic for a lot of tasks, especially tasks that require io. A task can be queued up and a callback can be attached to it. Then, while waiting for some condition to be met, your code can keep running. This results in "non-blocking code," which is familiar to all JS programmers.

Threads, in this context (web workers), refer to CPU cores. The Async model described above is all handled by a single CPU core. Most web applications don't require more than one core, but some do (or, at the very least, the demand is there). Using web workers, you can access other CPU cores, each of which has its own stack and its own separate Async event model.

The problem (from this article) with web workers is that data cannot be shared between CPU cores in JavaScript. Any data you want to pass from your main thread to a web worker must be copied, as in a bitwise copy. This article is about a solution for that, a way to share data between different threads in JavaScript, using a new standard that has been accepted by ECMA.

As far as your question, most of the time you don't want or need your promises or Async code to be handled in another thread. It would be insane to offload every single non-blocking line of code to another thread. Threads are much "heavier" than Async. Languages that have good support for threads also have Async.

However, wrapping web workers inside promises (or async/await) is absolutely something that makes sense, and something you can do now.

Well for one workers do not share scope, and at the moment sharing memory between the two is quite difficult. Second is race conditions like @btilly explained.

Right now workers are mainly used for extreme situations and eventually make it into libraries that others use.

I have created a library called Task.js that surfaces this idea into a promise compatible interface where you can just convert a pure function into a worker function. The end result is a promise supported function thats sends the function to a worker with your provided args and resolves when its done (also supports multiple workers and automatic queuing).

https://github.com/icodeforlove/task.js

Because you can't write and modify object that are shared with other thread while the process is going on without consequences:

    var str = '{}'
    await JSON.parseAsync(str);
    str = '{"foo": 1}' // this line might run before line above
Ah so you mean call other js processes through async/yield type stuff. I think that is cognitively easier than introducing new concepts like this has with a familiar way to do things. I agree with you. There would have to be some other syntax like "thread" to note it is not in the main thread but acted just like async or something.

var somevalue = async doSomething();

var someExpensiveVAlue = thread doExpensiveThing();

var lotsOfExpensiveThings = Thread.all(threads);

This is how it works in dotnet.

var val = await Task.Run(() => doSomeThreadedWork(param));

Indicates to the runtime that the specified delegate may be run on another thread. This doesn't explicitly start a new thread but rather just allows the delegate to execute on one of the thread pool threads that is managed by the runtime. It uses heuristics to decide how many threadpool threads to maintain and whether to actually use one of them to execute your delegate. A similar model would be very useful to have in JavaScript.

The issue is that in .Net there are locking primatives to access shared values... where as in Node/JS you would need something that only allowed passing of strings, other primatives, and SharedArrayBuffer or similar objects that don't change underneath unexpectedly.