Resolve JavaScript promises sequentially

Reading Time: 2 minutes 🕑

Last updated: September 27, 2022.

JavaScript promises are more flexible than callbacks, allowing asynchronous processes to be resolved in parallel.

But what if you want to replicate the sequential execution of tasks using promises?

Creating promises in the global scope and trying to wait for each one inside an async function using the await keyword await won’t work.

This is because JavaScript starts running a promise as soon as it encounters it when executing in a script. So all promises will start and finish at the same time:

const myPromise1 = new Promise(resolve => setTimeout(() => { return resolve("I'm done") }), 1000);
const myPromise2 = new Promise(resolve => setTimeout(() => { return resolve("Me too!") }), 1000);
const myPromise3 = new Promise(resolve => setTimeout(() => { return resolve("Finished!") }), 1000);

async function handlePromises() {
    const results = [];
    results.push(await myPromise1);
    console.log(results);
    results.push(await myPromise2);
    console.log(results);
    results.push(await myPromise3);
    console.log(results);
}

handlePromises(); // After two seconds, all three promises are pushed to the result array!

The solution is to wrap each of the promises in a function, and execute one after the other.

This way, each promise is only encountered by the JavaScript engine when its function is called:

const myPromise1 = () => new Promise(resolve => setTimeout(() => { return resolve("I'm done") }, 1000));
const myPromise2 = () => new Promise(resolve => setTimeout(() => { return resolve("Me too!") }, 1000));
const myPromise3 = () => new Promise(resolve => setTimeout(() => { return resolve("Finished!") }, 1000));

async function handlePromises() {
    const results = [];
    results.push(await myPromise1());
    console.log(results);
    results.push(await myPromise2());
    console.log(results);
    results.push(await myPromise3());
    console.log(results);
}

handlePromises();
// 1 sec: "I'm done" 
// 2 sec: "Me too!"
// 3 sec: "Finished!"

You can even take it a step further by loading each of the promises (still wrapped in a function) and loop through them using a for of…await loop.

In the loop, each function is called, with await used to wait for each one to return the promise result:

const myPromise1 = () => new Promise(resolve => setTimeout(() => { return resolve("I'm done") }, 1000));
const myPromise2 = () => new Promise(resolve => setTimeout(() => { return resolve("Me too!") }, 1000));
const myPromise3 = () => new Promise(resolve => setTimeout(() => { return resolve("Finished!") }, 1000));

const promises = [myPromise1, myPromise2, myPromise3];

async function handlePromises() {
    for (let promise of promises) {
        const results = [];
        const value = await promise();
        results.push(value);
        console.log(results);
    }
}

handlePromises();