Как обещать и ждать setImmediate в Node?

Я читал о том, как не блокировать цикл событий Node. Один из способов избежать блокировки - использовать разбиение.

Я пытаюсь использовать в своем коде секционированный цикл, но не могу дождаться своего цикла. Вот упрощенная версия моего кода:

    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);

Это возвращает "Report is { someValue: 1 }", но я бы хотел, чтобы someValue было 1000.

Я предполагаю, что setImmediate не возвращает обещание, поэтому я попытался его обещать:

    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);

Но это также возвращает "Report is { someValue: 1 }".

Итак, как я могу дождаться этого рекурсивного "цикла" setImmediate, чтобы я сообщал console.log только после завершения всего цикла рекурсии?

1 ответ

Решение

Когда вы обещали setImmediate, вы больше не передаете ему обратный вызов. Вместо этого вы простоawaitобещание, которое он возвращает. Затем вы выполните рекурсивный вызов:

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

Однако, async/await дает вам возможность просто написать реальный цикл:

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);

(Обратите внимание на небольшую разницу в рекурсии: условие уже проверено перед первой итерацией, а setImmediateвыполняется еще раз после последней итерации. Использоватьdo/while или даже while(true)+if(…)break; если необходимо.)

Кстати, если вы уже выполняете асинхронные (неблокирующие) операции внутри цикла, нет причин для добавления дополнительных setImmediateк нему. В руководстве рассматриваются только сложные синхронные вычисления, которые могут заблокировать цикл событий.

Другие вопросы по тегам