2

There are two async functions. One would set a variable, and the other one will wait for the vairable to change, like the code shown below. Even though the common practice is to use events or Promises, it was not viable in my situation.

function delay(ms: number): Promise<void> {
  return new Promise(resolve => {
    setTimeout(() => { resolve(); }, ms);
  });
}

function async fn1() {
  let done = false;
  (async () => {
    await delay(100); // simulate some work
    done = true;
  })();
  await (async () => {
    while (!done) { }
    // do other things
  })();
}

function async fn2() {
  let done = false;
  (async () => {
    await delay(100); // simulate some work
    done = true;
  })();
  await (async () => {
    while (!done) {
      await delay(200); // introduce some extra delay
    }
    // do other things
  })();
}

I wonder why the fn1 would stuck in the while loop but fn2 will work. Since the only difference between them is that the latter one introduced some extra waiting. Is this related to some underlying thread scheduling mechanisms of NodeJS/V8 interpreter?

I am using Node 12.18.3 and TypeScript 4.0.3.

EDIT: fixing the definition of fn2.

Daniel Lee
  • 161
  • 1
  • 9
  • during the time `while (!done) { } // do other things }` is running, unless `done` is set to true in `do other things` nothing else can execute ... because the call stack is never empty – Jaromanda X Sep 22 '20 at 03:33
  • Basically js is single threaded and if something is keeping it blocked like your busy loop it cannot go to other jobs unless it pauses – Shubham Srivastava Sep 22 '20 at 03:36
  • Look for javascript event queue or javascript call stack for some very good explanations of how this all works in JS – Jaromanda X Sep 22 '20 at 03:39
  • Just so you know, a busy wait loop is just generally the wrong type of design for an event driven, single threaded environment like nodejs. They can sometimes be made to work with promises and `await`, but even then, it's just better to use a promise that resolves when you're event occurs and just listen for that without any sort of busy wait loop. We could help you better with a good design if we saw real, actual code instead of this pseudo code that doesn't actually do anything useful and isn't a real world problem. – jfriend00 Sep 22 '20 at 04:02
  • So, to summarize, this is a wrong design path. Show us a real world problem (not pseudo code) and we'll help you with the right way to design for that actual problem. – jfriend00 Sep 22 '20 at 04:05

2 Answers2

1

Javascript is single threaded‡. Therefore any form of busy waiting will cause the thread to completely block preventing it from entering the event loop which means no event gets processed.

At the C level, the interpreter is very simple. Since it is single threaded it does not need any complicated logic, just a single infinite while loop. In pseudocode it goes something like this:

do {

    event_queue = execute_javascript()

    completed_events = wait_for_events()

    for_each (event from completed_events) {

        this_event = event_queue.find_one_matching(event)

        execute_javascript(this_event.callback)
    }

} while (there_is_pending_events)

As you can see, nothing in the interpreter runs in parallel. They all run sequentially. Asynchronous code waits in parallel but does not execute in parallel.

In your code, the while(){} loop gets stuck in execute_javascript() which will not return until your while loop completes. This means the interpreter is stuck.

Adding an await inside the while(){} loop causes execute_javascript() to return at the await (and will continue again at the line after await) allowing the interpreter to continue waiting for events at wait_for_events().

‡ Note: There are people who will tell you that javascript uses background threads to process asynchronous code. They are wrong. Node.js (not browsers) does use one, single, additional thread to process disk I/O but only disk I/O. Network I/O is done in the main thread like I describe above. The disk I/O is done that way because there is very little compatibility for asynchronous disk I/O API between Windows, Linux and BSD (BSD includes Mac OSX)

If you are interested in much lower level explanation for how the event loop and asynchronous code execution works in javascript you may be interested in my answers to these other related questions. In some I explain down to the level of how the hardware handles it. In some I explore how it's done in C/C++:

Is my understanding of asynchronous operations correct?

Is there any other way to implement a "listening" function without an infinite while loop?

node js - what happens to incoming events during callback excution

I know that callback function runs asynchronously, but why?

slebetman
  • 109,858
  • 19
  • 140
  • 171
0

For the first function fn1, you put async before the function, but with fn2 you did not. Try removing that from the first function and see if it still remains stuck.

cbilladeau
  • 41
  • 5
  • Sorry that was my mistake. `fn2` does have `async` modifier that I forgot to type. The original code won't compile if the `async` is missing. – Daniel Lee Sep 22 '20 at 03:49