Nodejs async loop is blocking React UI render

2.2k views Asked by At

I have an electron application where I need to send a lot of data through UDP. This process is started through a click of a button in my React UI, and this example simplifies my problem. My React button:

<button
  onClick={() => {
    foo();
  }}
>
  Run foo
</button>;

Foo function:

export const foo = async () => {
  while (true) {
    console.log("hello");
  }
};

While foo is asynchronous, it still freezes the React UI!

2

There are 2 answers

1
Wex On

Since JavaScript is single-threaded, as soon as your infinite while loop async function is called it will block your thread. You can get around this by introducing an await inside your while loop, which will allow your program to return to executing other parts of your code.

export const foo = async () => {
  while (true) {
    await new Promise(resolve => {
      resolve();
    });
  }
};
4
gcali On

An async function still blocks the main (and only) thread while it's being executed; async functions help usually because they call library functions that actually free up the main thread.

For instance, if instead of doing a simple infinite loop you did something like this:

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

function yieldThread() {
  return new Promise(resolve => resolve());
}

export const foo = async () => {
  while (true) {
    console.log("hello");
    await timeout(2000);
    //or
    await yieldThread();
  }
};

Your main thread wouldn't have been blocked.

Of course a timeout or a simple yield isn't usually the correct way to handle this situation, but it should exemplify the typical structure.

Assuming you have a function sendUdp(packet, callback) that has as first argument the packet you need to send and as second a callback without arguments when it has finished sending, you could do something like this:

export const foo = async () => {
  while (true) {
    const packet = getPacket();
    await new Promise(resolve => {
        sendUdp(packet, resolve);
    });
  }
};