Implement inSequence(tasks) to execute promise-returning functions sequentially

Tests async/await control flow versus Promise.all parallelism. A strong answer uses a for-of loop with await inside an async function, accumulates results in order, and stops cleanly on rejection. Red flag: suggesting Promise.all or forEach with await.
What's really being asked
This question tests whether you can orchestrate asynchronous operations sequentially rather than concurrently. Many senior engineers default to Promise.all for every array-of-promises scenario, so the interviewer wants to see if you understand when to block and await. It also surfaces knowledge of closure behavior inside loops, error propagation in async functions, and why certain array methods break sequential semantics.
The full answer
A good answer hits four things in order. First, declare an async function inSequence that accepts an array of task functions. Second, initialize an empty results array. Third, use a standard for loop or for-of loop to iterate the tasks, calling each one and awaiting the returned promise before pushing the result into the array. Fourth, return the results array, relying on the implicit promise wrapper of the async function. A senior candidate also mentions that if any task rejects, the async function automatically rejects and no subsequent tasks run, which is usually the desired behavior for true sequential dependency.
The mistakes people make
Common wrong answers reveal misunderstanding of the event loop. Using tasks.forEach with an async callback fires every task immediately because forEach does not await the callback it invokes. Returning Promise.all(tasks.map(t => t())) runs everything concurrently and defeats the purpose. Another red flag is a manual reduce chain building a promise ladder with .then; while it technically serializes, it is verbose, harder to debug, and often mishandles return types or errors. A subtle trap is forgetting that the array contains functions, not promises, so awaiting the element itself without calling it is a bug.
What usually comes next
Interviewers often follow up by asking how to add concurrency limits, such as running tasks in batches of three. They may also ask what happens if a task throws synchronously before returning a promise, or how to implement a retry with exponential backoff inside the loop. Another variant is returning results in the same order even when some tasks are allowed to run in parallel, which shifts the solution toward a Promise.all with index tracking or a task queue.
A concrete example
Imagine three tasks where task one takes 100 milliseconds, task two takes 50 milliseconds, and task three takes 25 milliseconds. With Promise.all the total time is roughly 100 milliseconds. With inSequence the total time is 175 milliseconds because each awaits the previous. A strong candidate might mention this trade-off explicitly and note that inSequence is appropriate when later tasks depend on earlier side effects, such as writing files in order or rate-limited API calls.
Interview question
Which pattern correctly implements inSequence to execute promise-returning tasks sequentially and return ordered results?
- a.Return Promise.all(tasks.map(task => task()))
- b.Use tasks.forEach with an async callback that awaits each task and pushes the result into an array
- c.Iterate with a standard for loop and await each tasks[i] without parentheses
- d.Declare an async function that iterates tasks with a for...of loop, awaits each task() call, and accumulates resultsCorrect
Why? this is the answer
A for...of loop inside an async function blocks on each awaited task call, guaranteeing sequential execution and ordered results. Using forEach with an async callback fires every task immediately because forEach does not await its callback, so tasks run concurrently and the function returns before they finish.
Just read this? Test yourself on what you have been reading.
Read the original → developer.mozilla.org
- #async-await
- #promises
- #typescript
- #sequential-execution
- #interview
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Every open role lists the topics its interview covers, so you can prepare for the real thing rather than guessing.
See open roles