105Implement an asynchronous dependency graph executor where tasks run only after their dependencies complete
hardAbout 60 minutesCodinggraphstopological-sortconcurrencypromises
Problem
Build systems, app start-up sequences and data loaders all run tasks that depend on other tasks. Implement runGraph(tasks, { concurrency = Infinity } = {}):
const results = await runGraph({
config: { deps: [], run: () => loadConfig() },
user: { deps: ["config"], run: ({ config }) => fetchUser(config.apiUrl) },
flags: { deps: ["config"], run: ({ config }) => fetchFlags(config.apiUrl) },
app: { deps: ["user", "flags"], run: ({ user, flags }) => startApp(user, flags) },
});
// user and flags run in parallel, after config, before app- Each task's
run(depResults)receives an object with the results of its dependencies and may be async. - A task starts as soon as all its dependencies have finished; independent tasks run in parallel, up to
concurrencyat a time. - Before running anything, validate the graph: a dependency that does not exist throws
Error('Unknown dependency "x" in task "y"'); a cycle throws anErrorwhose message contains the cycle path, for exampleCycle detected: a -> b -> c -> a. - If a task fails, start no new tasks and reject with an error whose message names the task and includes the original message, with the original as
cause. Tasks already running may finish. - Resolve with an object of every task's result.
Try it yourself
Write your solution, run it, then submit to check it against the tests.
solution.jsJavaScript
Press Run or ⌘ Enter to execute your code.
Step-by-step
Loop over every task's deps and throw for any name that is not a task. Doing this up front means a typo fails immediately instead of hanging forever.
Unlock the full solution
What's inside
- Step-by-step
- Approach
- Solution
- Complexity
- Edge cases
- Follow-up questions
- Tests
Includes 2 code listings, 5 edge cases, 4 follow-up questions.
One-time payment by UPI, card or net banking. Launch price.