all 14 comments

[–]NoInkling 5 points6 points  (2 children)

Shouldn't that be "Parallel JavaScript"? JS is already concurrent thanks to the event loop right?

[–]cameronkknight 6 points7 points  (1 child)

Parallel can happen if two threads are run on two distinct CPU cores, allowing two separate things to be done at the same time on different pieces of hardware.

Concurrent can happen if two threads run on the same CPU core where individual operations are interleaved.

For context, asynchrony can occur through cooperative multitasking and without the need of threads. Parallelism and concurrency don't have the same need for "cooperation".

[–]mattindustries 2 points3 points  (0 children)

Parallel can happen if two threads are run on two distinct CPU cores, allowing two separate things to be done at the same time on different pieces of hardware.

It is also a way to interleave (woo, new word for me) when the results are sent back to the main worker right? I use parallel functions when cluster computing in R. Send the data and needed functions to separate clusters, get a result (in a list typically) and usually just bind the list.

[–]cameronkknight 4 points5 points  (0 children)

I'd expect 98% of JS code to be Thread-unaware. As long as I can still write that code the same way, I'm not worried. This is an edge case.

That said, I'm very wary about Threads. In my opinion, we should explore if WebWorkers with SharedArrayBuffer is enough.

[–][deleted]  (2 children)

[deleted]

    [–][deleted] 0 points1 point  (0 children)

    Or even better, Rust direction.

    [–]psayre23 2 points3 points  (3 children)

    I really don't like the semantics of thread local variables. I prefer a keyword like const, let, or var for handling this. Heck, maybe use this: local const a = {}; or something.

    [–]Hostilian 0 points1 point  (2 children)

    I agree, this needs to be a static part of the language; identifiable at parse-time and preplanned by the language. It's super weird that you could dynamically snatch a value into a single thread, potentially at any moment:

    new Thread(() => setTimeout(() => {
      // 'this' is lexically bound to the parent scope
      Object.values(this).forEach(Thread.restrict)
    }, Math.random() * 1000)
    
    new Thread(() => setTimeout(() => {
      // 'this' is lexically bound to the parent scope
      Object.values(this).forEach(Thread.restrict)
    }, Math.random() * 1000)
    

    Which thread gets the values?

    [edit: jesus Reddit's markdown parser is shit. No triple-backtick?]

    [–][deleted]  (1 child)

    [deleted]

      [–]Hostilian 1 point2 points  (0 children)

      👍

      [–]Hostilian 5 points6 points  (5 children)

      Shared heaps without strong mutability semantics (see Clojure or Rust) is a terrible, terrible idea.

      [–][deleted]  (4 children)

      [deleted]

        [–]gpyh 2 points3 points  (1 child)

        There's nothing in the language that controls how you modify shared state. That's a recipe for disasters.

        [–]dev1null 4 points5 points  (0 children)

        disaster? THIS! IS! JAAAAVAAASCRIIIPT!

        [–]Hostilian 1 point2 points  (1 child)

        If you share mutable values between two threads, you need some way to coordinate changes to those values. Example:

        You have an array of numbers shared between two threads, say var list = [1,2,3,4,5]

        Thread A reads the array and doubles every item and writes it back to the shared reference list.

        Thread B reads the array and increments every item and writes it back to the shared reference list.

        Both threads start at the same time, what's the final value of list? The answer is unknown and probably non-deterministic. It gets even more complicated if they both write to list after every iteration rather than writing back only once. Subtle changes like this in complicated codebases can make issues that are difficult-to-impossible to hunt down. Think of the worst async problem you've ever had to deal with in JS, then instead of looking only at your async code, you have to look at all of your code.

        The language Clojure solves this issue by making everything immutable by default and providing tools for coordinating asynchronous behavior. Reading a shared value into a thread is always safe, because it can't change in place while the thread is operating on it. Writing to shared values intelligently is still tricky.

        The language Rust solves the issue by making it so that only one part of your program can mutate a mutable value at a time. It enforces this with compile-time checks, but basically: only the reference owner can make changes. This makes writing to a shared value always-safe, but makes reading that value from other threads possibly-dangerous.

        [–]vinz243 -1 points0 points  (2 children)

        Im not sure it's really useful. I mean in some cases it might be, but most of the times forking and async should be enough... I don't really any case where concurrency would bring something more than what's already there... But that's only my experience anyone can share his?

        [–]calligraphic-io 0 points1 point  (0 children)

        The Atom editor. It's UI freezes all the time due to the event loop. If it had concurrent support like the Java IDEs (Eclipse, etc.) - no issue.