Skip to content

Instantly share code, notes, and snippets.

@RobertFischer
Last active March 5, 2020 20:16
Show Gist options
  • Save RobertFischer/32909ccbd5821320b600c320304e57f1 to your computer and use it in GitHub Desktop.
Save RobertFischer/32909ccbd5821320b600c320304e57f1 to your computer and use it in GitHub Desktop.
Node concurrency
/*
Node itself is single-threaded. There is some concurrency stuff that comes from calling out to
C libraries and I/O, but it can be ignored unless you’re performance-tuning: in general, think of
your entire application as executing on one single thread.
Asynchronous actions in Node are all callback-based (“continuation passing style”, or CPS): at
any point when Node needs to perform something asynchronously (eg: I/O), the Node API will take
a callback and invokes that callback with the results. The convention for those callbacks is that
the first argument is the error (if any), and the rest of the arguments are the results.
Promises are simply wrappers around callbacks. Here's something to give you the basic idea:
*/
const STATE_PENDING = 0;
const STATE_REJECTED = 1;
const STATE_RESOLVED = 2;
class Promise {
constructor(fn) {
this.state = STATE_PENDING;
this.result = null;
this.thens = [];
this.catches = [];
fn( (err, returned) => {
if(err) {
this.state = STATE_REJECTED;
this.result = err;
} else {
this.state = STATE_RESOLVED;
this.result = returned;
}
this._fire();
});
}
_fire() {
if(this.state === STATE_REJECTED) {
this.thens = null;
while(!_.isEmpty(this.catches)) {
this.catches.shift()(this.result);
}
} else if(this.state === STATE_RESOLVED) {
this.catches = null;
while(!_.isEmpty(this.thens)) {
this.thens.shift()(this.result);
}
}
}
then(fn) {
this.thens.push(fn);
this._fire();
}
catch(fn) {
this.catches.push(fn);
this._fire();
}
}
/*
Similarly, `async` and `await` are simply wrappers around promises.
*/
// "async function foo() { /* code here */ }" is equivalent to:
function foo() {
return Promise.try( () => { /* code here */ } );
}
// "const bar = await foo(); /* rest of the code */" is equivalent to:
foo().then( (bar) => { /* rest of the code */ } );
/*
In concurrent runtimes, two (or more) threads of execution can be executing at the same time.
In those runtimes, you can't assume that any line of code is immediately executed after the last.
Consider the following:
*/
function addFoos(left, right) {
const leftFoo = left.foo;
const rightFoo = right.foo;
return leftFoo + rightFoo;
}
/*
In a concurrent runtime, we couldn't assume that the value of "leftFoo" is equal to "left.foo" on the last line:
it's possible that "const leftFoo = left.foo" was executed, and then another thread of execution set
"left.foo = left.foo+1", and then our thread of execution continued. Our thread of execution wouldn't see the
change in "left.foo", because we saved off a copy before the change occurred.
***Node is not a concurrent runtime; Node is an asynchronous runtime.*** This means that you don't ever have
multiple threads of execution, and you *can* assume that "leftFoo" still equals "left.foo" at the "return" above.
When Node needs to do things asynchronously, it will take a callback (which may be wrapped up into a Promise).
That callback will be executed on the single thread of execution when it has a result or error.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment