Skip to content

Latest commit

 

History

History
64 lines (56 loc) · 2.44 KB

Promise.race.md

File metadata and controls

64 lines (56 loc) · 2.44 KB

Not used code

Details

When you call Promise.race with a long-running promise, the resolved value of the returned promise gets retained for as long as each of its promises do not settle. Example code to demonstrate:

async function randomString(length) {
    await new Promise((resolve) => setTimeout(resolve, 1));
    let result = "";
    const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
    for (let i = 0; i < length; i++) {
        result += characters.charAt(Math.floor(Math.random() * characters.length));
    }
    return result;
}

(async function main() {
    let i = 0;
    const pending = new Promise(() => {});
    while (true) {
        await Promise.race([pending, randomString(10000)]);
        if (i++ % 1000 === 0) {
            const usage = process.memoryUsage();
            const rss = Math.round(usage.rss / (1024 ** 2) * 100) / 100;
            const heapUsed = Math.round(usage.heapUsed / (1024 ** 2) * 100) / 100;
            console.log(`RSS: ${rss} MiB, Heap Used: ${heapUsed} MiB`);
        }
    }
})();

In this example, we pass a large random string along with a non-settling promise to Promise.race, and the result is that every single one of these strings is retained.

How to fix: Avoid Promise.race. Replacing your "await Promise.race" expressions with "await new Promise" expressions, where the newly constructed Promise sets a function variable in the outer scope.

(async function main() {
    let i = 0;
    // These functions are set in a promise constructor later
    let resolve;
    let reject;

    const pending = new Promise(() => {});
    pending.then((value) => resolve(value), (err) => reject(err));

    while (true) {
        randomString(10000).then((value) =>  resolve(value), (err) => reject(err));
        // This is the await call which replaces the `await Promise.race` expression in the leaking example.
        // It sets callbacks for our promises
        await new Promise((resolve1, reject1) => {
            resolve = resolve1;
            reject = reject1;
        });
        if (i++ % 1000 === 0) {
            const usage = process.memoryUsage();
            const rss = Math.round(usage.rss / (1024 ** 2) * 100) / 100;
            const heapUsed = Math.round(usage.heapUsed / (1024 ** 2) * 100) / 100;
            console.log(`RSS: ${rss} MiB, Heap Used: ${heapUsed} MiB`);
        }
    }
})();