trying to understand async / await / sync in node

i know this probably has been asked before, but coming from single-threaded language for the past 20 years, i am really struggling to grasp the true nature of node. believe me, i have read a bunch of SO posts, github discussions, and articles about this.

i think i understand that each function has it’s own thread type of deal. however, there are some cases where i want my code to be fully synchronous (fire one function after the other).

for example, i made 3 functions which seem to show me how node’s async i/o works:

function sleepA() {
    setTimeout(() => {
        console.log('sleep A')
    }, 3000)
}

function sleepB() {
    setTimeout(() => {
        console.log('sleep B')
    }, 2000)
}

function sleepC() {
    setTimeout(() => {
        console.log('sleep C')
    }, 1000)
}

sleepA()
sleepB()
sleepC()

this outputs the following:

sleep C
sleep B
sleep A

ok so that makes sense. node is firing all of the functions at the same time, and whichever ones complete first get logged to the console. kind of like watching horses race. node fires a gun and the functions take off, and the fastest one wins.

so now how would i go about making these synchronous so that the order is always A, B, C, regardless of the setTimeout number?

i’ve dabbled with things like bluebird and the util node library and am still kind of confused. i think this async logic is insanely powerful despite my inability to truly grasp it. php has destroyed my mind.

>Solution :

how would i go about making these synchronous so that the order is always A, B, C

You’ve confirmed that what you mean by that is that you don’t want to start the timeout for B until A has finished (etc.).

Here in 2021, the easiest way to do that is to use a promise-enabled wrapper for setTimeout, like this one:

const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));

Then, make sleepA and such async functions:

async function sleepA() {
    await delay(3000);
    console.log("sleep A");
}

async function sleepB() {
    await delay(2000);
    console.log("sleep B");
}

async function sleepC() {
    await delay(1000);
    console.log("sleep C");
}

Then your code can await the promise each of those functions returns, which prevents the code from continuing until the promise is settled:

await sleepA();
await sleepB();
await sleepC();

You can do that inside an async function or (in modern versions of Node.js) at the top level of a module. You can’t use await in a synchronous function, since await is asynchronous by nature.

i think i understand that each function has it’s own thread type of deal

No, JavaScript functions are just like those of other synchronous languages (despite its reputation, JavaScript itself is overwhelmingly synchronous; it’s just used in highly-asynchronous environments and recently got some features to make that easier). More on this below.

ok so that makes sense. node is firing all of the functions at the same time, and whichever ones complete first get logged to the console. kind of like watching horses race. node fires a gun and the functions take off, and the fastest one wins.

Close. The functions run in order, but all that each of them does is set up a timer and return. They don’t wait for the timer to fire. Later, when it does fire, it calls the callback you passed into setTimeout.

JavaScript has a main thread and everything runs on that main thread unless you explicitly make it run on a worker thread or in a child process. Node.js is highly asynchronous (as are web browsers, the other place JavaScript is mostly used), but your JavaScript code all runs on one thread by default. There’s a loop that processes "jobs." Jobs get queued by events (such as a timer firing), and then processed in order by the event loop. I go into a bit more on it here, and the Node.js documentation covers their loop here (I think it’s slightly out of date, but the principal hasn’t changed).

Leave a Reply