0

I have to keep making calls to SERVICE_PATH until the report in the response ready.
Apparently, below code is blocking due to the usage of "do/while".
So, the question is how can I restructure the code to make it non-blocking?

let scoring_report;
let timeout_count = 1;
let getReport;
do {
    await new Promise((resolve) => setTimeout(resolve, timeout_count * 1000));
    getReport = await axios.post(`${process.env.SERVICE_PATH}:8001`, {body});
    scoring_report = getReport.data.data.reportBase64;
    timeout_count += 2;
  } while (!scoring_report);
Rass_Rukh
  • 25
  • 5
  • It's better either to use worker threads or some scheduler to execute once in N minutes/seconds – Anatoly Feb 17 '22 at 06:28
  • "*Apparently, below code is blocking due to the usage of "do/while"*". No it's not. – Kaiido Feb 17 '22 at 06:52
  • @Kaiido I thought JS code usually is responsible for blocking since main-thread is busy and dont let Event loop interfere while main-thread is computing. Can you elaborate please? – Rass_Rukh Feb 17 '22 at 06:59
  • Well your async function is being paused by the `await` keyword, it will resume only when the Promise it awaits resolves, and this will happen only after the timeout's callback is executed. In between, this script will do nothing and won't keep the event-loop busy. – Kaiido Feb 17 '22 at 07:03
  • @Kaiido i see, but in my case do/while depends on scoring_report which is a response of POST request(getReport). So, do/while keeps the main thread busy while asynchronous request(getReport) keeps on calling until it receives a response which has a "reportBase64". That's why I thought do/while is blocking. Am I misunderstanding something here? – Rass_Rukh Feb 17 '22 at 07:39
  • Yes, you are misunderstanding how `async` functions work. If you will it's a bit like `setTimeout(() => axios.post(...).then( setTimeout(()=> axios.post(...).then... , timeout_count * 1000))` Between all these callbacks, the event loop is free. Well it's the same in your `while` loop, at each `await` the event loop is free, which represents the most part of this script. – Kaiido Feb 17 '22 at 07:44
  • To convince yourself, just add a `console.log("synchronous");`right after you call this async function: https://jsfiddle.net/4Lpow3uv/ Ps: Does [this Q/A](https://stackoverflow.com/questions/57160341/how-does-javascript-async-await-actually-work) answers your question? – Kaiido Feb 17 '22 at 07:56

1 Answers1

-1

Your code is blocking not due to do... while... Its due to the async await.

Sample blocking code.

async function resolveAfterSomeTime() {
  let counter = 0;
  do {
    console.log("starting promise")
    const x = await new Promise(resolve => {
      setTimeout(function () {
        resolve("done")
        console.log("promise is done")
      }, 800)
    });
    counter++;
    console.log(x);
  } while (counter < 5);
}
resolveAfterSomeTime();

As you can see the above code is blocking for 800 milli second for each execution due to async await.

You can make this non blocking by simply removing async await. If you are not interested in the result, you can simply call the function and go for the next iterearion. Here in the below code, the value for x will be a promise, you can check the resolved status of the promise to handle your further logic using x.then((resp) => console.log(resp))

Sample code.

function resolveAfterSomeTime() {
  let counter = 0;
  do {
    console.log("starting promise")
    const x = new Promise(resolve => {
      setTimeout(function () {
        console.log("promise is done");
        resolve("done");
      }, 800)
    });
    counter++;
    x.then((resp) => console.log(resp))
  } while (counter < 5);
}
resolveAfterSomeTime();

Your sample non blocking code

let scoring_report;
let timeout_count = 1;
let getReport;
do {
  new Promise((resolve) => setTimeout(resolve, timeout_count * 1000));
  // here getReport will be a promise
  getReport = axios.post(`${process.env.SERVICE_PATH}:8001`, { body });
  // Use getReport.then((data) => {}) for further logic
} while (!scoring_report);

Implementation using setInterval

If you just want to execute the function on the specified interval, you can make use of setInterval which will not block your main thread. Donot forget to clearInterval once the required result is available.

Sample Implementation

let scoring_report;
let timeout_count = 1;
let getReport;

const myInterval = setInterval(() => {
  getReport = await axios.post(`${process.env.SERVICE_PATH}:8001`, { body });
  scoring_report = getReport.data.data.reportBase64;
  timeout_count += 2;
  if(scoring_report) {
    clearInterval(myInterval)
  }
}, timeout_count * 1000);
Nitheesh
  • 19,238
  • 3
  • 22
  • 49
  • I did the scoring_report check inside getReport.then like this getReport.then( (data)=> scoringreport =data.data.reportBase64 ) and I got this error: FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory – Rass_Rukh Feb 17 '22 at 06:46
  • 1
    It's not "blocking", it's paused. If something else has to be done while awaiting the timer, it will be done. That's what we call "non-blocking". – Kaiido Feb 17 '22 at 06:51
  • @Rass_Rukh what is the purpose of this code? `new Promise((resolve) => setTimeout(resolve, timeout_count * 1000));` – Nitheesh Feb 17 '22 at 06:57
  • @Nitheesh the purpose of it is just to wait (n*1sec) and then make a request(getReport). What I need is to make repetitive requests(every n*1sec or 1 min) until SERVICE_PATH has a getReport in the response. – Rass_Rukh Feb 17 '22 at 07:33
  • @Rass_Rukh Better use `setInterval` instead of async await. Upfated the answer with this implementation. – Nitheesh Feb 17 '22 at 08:10