To execute promises in sequence, chain them using reduce or an async loop.
β Using Array.prototype.reduce
function runSequential(promises) { return promises.reduce( (chain, current) => chain.then(() => current()), Promise.resolve() ); }
Each
currentmust be a function returning a promise.
π§ͺ Example:
const tasks = [ () => Promise.resolve(console.log("1")), () => new Promise(res => setTimeout(() => { console.log("2"); res(); }, 1000)), () => Promise.resolve(console.log("3")) ]; runSequential(tasks);
Output:
1
(wait 1s)
2
3
β 2. Using async/await loop (more readable)
async function runSequential(tasks) { for (const task of tasks) { await task(); } }
Same usage as above.
Notes:
- Promises start only after the previous one completes .
- Use when order matters (e.g. file writes, dependent API calls).
If each promise depends on the result of the previous , you need to pass the previous result forward . Use an async/await loop and chain results explicitly.
β Pattern: Sequential dependent promises
async function runSequential(tasks, initialValue) { let result = initialValue; for (const task of tasks) { result = await task(result); } return result; }
Each task is a function that accepts the previous result and returns a promise.
π§ͺ Example:
const tasks = [ (x) => Promise.resolve(x + 1), // 1 β 2 (x) => Promise.resolve(x * 3), // 2 β 6 (x) => new Promise(res => setTimeout(() => res(x - 4), 500)) // 6 β 2 ]; runSequential(tasks, 1).then(console.log); // β 2
β Behavior:
- Passes value from one promise to the next.
- Works with both sync and async returns.
- Use case: chained API calls, data transformation pipelines.
Quick Quiz
Test your understanding with 3 quick questions
Q1Why must the tasks array contain functions that return promises, rather than promises directly?
Q2What is the purpose of `Promise.resolve()` as the initial value in the reduce pattern?
Q3When should you use sequential promise execution instead of Promise.all?