
A guide to threads in Node.js - bibyte
https://blog.logrocket.com/a-complete-guide-to-threads-in-node-js-4fa3898fe74f
======
tannhaeuser
> _It was only in 2009 that Ryan Dahl, creator of Node.js, made it possible
> for developers to use the language to write back-end code_

Server-side JavaScript was a thing _long_ before that. In fact, Netscape used
it as early as 1994 [1], predating Java as a backend language. And CommonJS
(on what nodejs' modules and many APIs are based) was a community effort
towards a common API by 2000s SSJS implementations (helma, v8cgi/teajs, and
others).

Apart from that, a nice read for those who need a WebWorker-like API for CPU-
bound nodejs tasks.

[1]: [https://en.wikipedia.org/wiki/Server-
side_scripting](https://en.wikipedia.org/wiki/Server-side_scripting)

[2]:
[http://wiki.commonjs.org/wiki/CommonJS](http://wiki.commonjs.org/wiki/CommonJS)

~~~
denysonique
There was also Aptana Jaxer around 2008:
[https://en.wikipedia.org/wiki/Aptana#Aptana_Jaxer](https://en.wikipedia.org/wiki/Aptana#Aptana_Jaxer)
[http://www.jaxer.org/](http://www.jaxer.org/)

~~~
bryanrasmussen
I wish Aptana had won the race, or at least if not beat Node, competed
reasonably with Node.

------
crabasa
_> type WorkerCallback = (err: any, result?: any) => any;

export function runWorker(path: string, cb: WorkerCallback, workerData: object
| null = null) { const worker = new Worker(path, { workerData });

worker.on('message', cb.bind(null, null)); worker.on('error', cb);

worker.on('exit', (exitCode) => { if (exitCode === 0) { return null; }

    
    
       return cb(new Error(`Worker has stopped with code ${exitCode}`));
     });
    
     return worker;

}_

For an article titled "A Guide to Threads in Node.js", I really wish the first
example of writing a thread wasn't in TypeScript.

~~~
danenania
Why? Just think of it as inline documentation. It in no way changes the
semantics of the javascript and efficiently communicates how the API works.

~~~
crabasa
When writing a developer guide, especially to something as fundamental as
"threads in Node.js", it pays to:

1) Make as few assumptions as possible regarding what the reader knows or has
experience with

2) Make code samples as complete and "copy-and-pastable" as possible.

You're assuming that all developers reading this guide will effortlessly
translate that TS code to JS.

Using TS creates a possible barrier to understanding, which I'm sure isn't the
intent of someone publishing such a guide. Better to simply write some
comments or augment the prose in the guide.

~~~
danenania
Ok, fair enough. But for anyone who has some familiarity with type systems,
the _lack_ of type signatures (or needing to mentally parse them in some other
non-standard format) also acts as a barrier to understanding. Perhaps either a
side-by-side view or a tab switcher would be ideal.

~~~
crabasa
Except this is a guide for _Node.js (JavaScript)_. The one assumption the
author _can_ make is that the reader knows and is comfortable with JavsScript.

~~~
danenania
Yeah I'm not trying to disagree with you there, just pointing out that TS
types are an efficient way to add important context that many (like me) will
appreciate, and as a popular well-defined standard I think they are superior
to some other kind of ad hoc spec that is included in comments or prose or
whatever.

I think it would be great if more JS code examples started including type
signatures, since they are useful whether or not you program in TS. But yes,
there should probably also be a JS-only example in that case and/or a way to
toggle them off.

~~~
hackerbob
We've been throw this game before: CoffeeScript

Until TS becomes an official standard supported by ECMAScript out of the box.
Were just going along with what feels or looks good and the javascript
community has proven that can change from year to year.

~~~
danenania
Well, I was huge fan of CoffeeScript, so you’ve got me there. I don’t regret a
single one of the (many) times I used it though. It made me and the teams I
was part of significantly more productive and allowed us to produce more
concise and maintainable code than we could have with plain JS. I feel the
same about TypeScript today except honestly the productivity gains are a lot
more dramatic.

~~~
Yaggo
As a former coffeescript fan, doesn’t the extra “clutter” in typescript syntax
bother you?

I like CS very much myself. While I can see the benefit of having type system
(in certain projects), I just can’t get over the aesthetic/syntax issue.

~~~
true_religion
For me, not really. If I want to use type annotation, I need to write the
types somewhere in order to communicate it to future readers.

I would much rather do it via Typescripts annotations than JSDocs.

It's not clutter as it was deemed necessary by the writer (me).

If I don't want to use type annotations, I simply don't add them and
Typescript does not force you to do so unless you tell it to.

------
com2kid
I wasn't a huge fan of the "single thread for everything" paradigm, then I
helped design a single threaded cooperatively multitasked embedded OS in
C/C++.

No overhead from context switches. Everyone who is running knows how long they
have to run, and knows that they are NOT going to get interrupted. No need to
worry about locks or how to share data. Want to pass data to another module?
Just pass it through well defined interfaces and you darn well know there will
never be a read/write conflict, and the next time that module's code runs,
it'll have access to that data. (No queue!)

The only exception, and what made it all possible, was the interrupt routines
from hardware[1]. Anyone who subscribed to hardware events (entire OS was
subscription based, no polling reads ever) had to implement a "thunk pattern"
to put data into a receive buffer. The design pattern to do this was the same
everywhere in all modules, making code understandable across the entire
project.

It is an incredibly freeing paradigm to write in. It becomes so much easier to
prove[2] the correctness of code when you can read all the code straight
through and not have to ever worry about someone stomping on your data.

It wasn't an RTOS, but even so, it becomes really easy to start providing
performance guarantees.

Internal builds had a watchdog timer[3] that would crash the device if it
wasn't 'kicked' every so often. Set that to 3ms, start working with the code,
and the stack traces tell you instantly who is over their CPU budget. Rewrite
code and break it apart into multiple chunks that are scheduled for later
execution, repeat until everyone is under their CPU allotment.

For many tasks, single threaded code is _nice_. Getting rid of preemption is
even nicer.

Preemptive multithreading is a compromise. It means that no one thread/process
can bring down the system by hogging 100% of resources, but it also creates a
huge overhead where important work, work that makes for a better user
experience, can (will!) get interrupted for work that honestly doesn't need to
be done right now.

The solution to this is just throw so much CPU at the problem that everything
gets done in a reasonable amount of time. It has often been noted that
"reasonable amount of time" means systems today are less responsive than a 486
running DOS from 1992.

Of course it isn't reasonable to have a modern cooperatively multithreaded
consumer OS, no way would the hundreds of processes ran at any one time all
cooperate with each other.

But if you ever get a chance to write code on a single threaded cooperative
system, go do it. It is a lot of fun.

Now all this meant that going from embedded C to NodeJS wasn't that large of a
mental leap! Not having to directly read bytes off the wire was weird
(seriously, took a bit of getting use to), but it turns out that "get data, do
work, schedule what needs to be done, return early" ends up being the same
paradigm at both the top and the bottom of the programming stacks!

[1] All I/O was done using DMA engines, basically a fancy limited programmable
piece of hardware that can read and write to all the different pieces of HW
hanging off of the main chip, so for example as Bluetooth packets come in, the
DMA engine shoves the packets into a buffer and when the buffer is full it
raises an interrupt that lets the CPU know that data is waiting. It looks
almost exactly like async I/O in any of the modern programming languages,
except you have direct access to all that IO being _hardware offloaded_.
Writing to the bare metal rocks.

[2] For a reasonable enough degree of "prove" that software is reliable and
doesn't crash from threading issues

[3] A watchdog timer is a physical timer hooked across the power lines of your
chip. If it isn't activated every so often (in the industry this is called
"kicking the watchdog") it will, in debug builds it does a controlled crash of
the CPU, and in retail builds it will reset the entire system. If you've ever
had an embedded device reset itself before your very eyes, it is becomes the
CPU got locked up and no one kicked the watchdog, so the entire system
emergency reset itself.

~~~
tonyarkles
Now and then I get to work in systems like this, and you're right. It's a joy!
And thank you, I really like the idea of setting the watchdog to really small
timeouts to put upper bounds on execution time in debug builds. That's a
fantastically clever use of it!

For the record, I much prefer the term "pet the watchdog" :).

~~~
com2kid
>For the record, I much prefer the term "pet the watchdog" :).

I think the EEs who setup the watchdog were far too grizzled. :D

~~~
tonyarkles
Hah! I've only got a little bit of grey in my beard so far...

------
Trisell
Honest question. Are we going to be able to pass functions with data to
threads? Or are these only designed to use files? Which makes this feel like a
better wrapper on the child process api that already exists.

~~~
ryanpetrich
The code to run in the worker is passed as a string or a path, so no data can
be captured. Data can be sent to the worker by posting messages, but the data
is never shared—only copied. A limited set of types can be transferred to a
worker, after which point they will become unusable in the parent.

~~~
11235813213455
For sharing (byte) data, I think there is SharedArrayBuffer
[https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer)

But else, yes, the sent and received data is serialized (when using ipc, else
you can also send raw data through streams, and handle the serialization
yourself)

~~~
rndgermandude
SharedArrayBuffer -> shared

ArrayBuffer -> ownership can be transferred around, but the data can only be
owned by one thread at a time. The data itself isn't copied. Passing around
ArrayBuffers back and forth is good enough for a lot of stuff. In a worker you
can then put a node Buffer, TypedArray, or DataView around the ArrayBuffer
again, if you want.

------
Scarbutt
With the javascript community fixing most of it shortcomings, I suspect in a
few years JS/TS is going to be way far ahead of any programming language in
term of users.

~~~
GordonS
Interesting that you say JS/TS - while Typescript of course transpile to
JavaScript, the 2 languages take very different approaches to something that
really divides devs - the question of static vs dynamic typing.

~~~
mynegation
Exactly. Together, this pair of languages will appeal to a broader range of
developers - those preferring dynamic typing, those preferring static typic,
and pragmatists who start the quick prototype in JS and move to TS for better
maintainability.

------
sbhn
A good video on parallel processing CPU bound taks with nodejs.
[https://youtu.be/ZYfSe9qKaZE](https://youtu.be/ZYfSe9qKaZE)

------
dboreham
Haven't read the article but I'm assuming it's an empty page?

<ducks>

~~~
hombre_fatal
No.

