Sequential Async
Prompt
Write a function executeInSeries that takes an array of asynchronous functions and executes them one after another, in order. Each function should only start after the previous one has completed.
- The function should accept an array of async functions as input.
- Each function in the array must finish executing before the next one starts.
- The function should return a Promise that resolves when all functions have completed.
Playground
A for...of loop combined with await will naturally
pause at each iteration until the current async function
completes before moving to the next.
Be careful not to use forEach here. forEach doesn't
respect await inside the callback, so all the functions
would fire at roughly the same time.
Remember to declare the function as async so you can use
await inside the loop body.
Solution
Explanation
This problem is deceptively simple. The entire solution is just a for...of loop with await, but understanding why it works (and why the obvious alternatives don't) is what interviewers are really testing.
async function executeInSeries(functions) {
for (const fn of functions) {
await fn();
}
}When you await fn() inside a for...of loop, JavaScript pauses the loop at that line until the promise returned by fn() settles. Only then does it move to the next iteration. This gives you true sequential execution: function 1 finishes, then function 2 starts, then function 2 finishes, then function 3 starts, and so on.
The biggest trap candidates fall into is reaching for forEach. It feels natural because it's the "modern" way to iterate arrays, but forEach completely ignores await. Here's why: forEach accepts a callback, and it calls that callback synchronously for each element. Even if your callback is async, forEach doesn't wait for the returned promise to resolve. It just fires all callbacks back-to-back, and all the async functions end up running in parallel.
// This does NOT run sequentially
functions.forEach(async (fn) => {
await fn();
});The for...of loop doesn't have this problem because await directly pauses the async function that contains it. There's no callback indirection.
You could also solve this with reduce and promise chaining, something like functions.reduce((chain, fn) => chain.then(fn), Promise.resolve()). That works too, but the for...of version is much easier to read and reason about. In an interview, clarity matters.
Common mistakes
Forgetting to declare the function as async means you can't use await inside it. And using Promise.all(functions.map(fn => fn())) would run everything in parallel, which is the opposite of what we want here.