3

I've been reading about how to not block Node's event loop. One way to avoid blocking is to use partitioning.

I'm trying to use a partitioned loop in my code, but I cannot seem to await for my loop. Here's a simplified version of my code:

    const report = {
        someValue: 0
    };

    const runLoop = async () => {
        report.someValue += 1;

        // all sorts of async operations here that use async-await

        if (report.someValue < 1000) {
            await setImmediate(runLoop);
        }
    };

    await runLoop();
    console.log('Report is', report);

This returns "Report is { someValue: 1 }", but I'd want someValue to be 1000.

I'm guessing setImmediate doesn't return a promise, so I've tried promisifying it:

    const setImmediatePromise = util.promisify(setImmediate);

    const report = {
        someValue: 0
    };

    const runLoop = async () => {
        report.someValue += 1;

        // all sorts of async operations here that use async-await

        if (report.someValue < 1000) {
            await setImmediatePromise(runLoop);
        }
    };

    await runLoop();
    console.log('Report is', report);

But this also returns "Report is { someValue: 1 }".

So, how can I await for this recursive setImmediate "loop" so that I console.log report only after the entire recursion cycle is finished?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
iepure
  • 249
  • 2
  • 14

1 Answers1

7

When you have promisified setImmediate, you no longer pass a callback to it. Instead, you just await the promise it returns. Then afterwards you'd do the recursive call:

async function runLoop() {
    …
    if (…) {
        await setImmediatePromise();
        return runLoop();
    }
}

However, async/await gives you the capability to just write an actual loop:

const setImmediatePromise = util.promisify(setImmediate);

const report = {
    someValue: 0
};

while (report.someValue < 1000) {
    report.someValue += 1;
    // all sorts of synchronous operations here
    await setImmediatePromise();
}

console.log('Report is', report);

(Notice the slight difference to the recursion: the condition is already checked before the first iteration, and the setImmediate runs once more after the last iteration. Use do/while or even while(true)+if(…)break; if necessary.)

Btw, if you are already doing asynchronous (non-blocking) operations inside the loop, there is no reason to add an extra setImmediate to it. The guide only deals with complex synchronous calculations that would block the event loop.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • This seems to work, thanks! Did I understand correctly: In your example the `await setImmediatePromise();` that's inside the while loop makes that while loop non-blocking? – iepure Jun 15 '20 at 10:06
  • 1
    @iepure Yes. Make sure to understand [how the promisification works](https://stackoverflow.com/q/22519784/1048572). Try writing the same with `new Promise` instead of `util.promisify` (for practice only). – Bergi Jun 15 '20 at 10:09