Control Task
Not all features are equally important. An analytics tracker failing is acceptable — a missing user session is not. When a critical feature fails, the app shouldn’t silently show a broken page; it should acknowledge the problem.
A control task is a pattern for this. It observes the statuses of critical tasks and returns a result — { passed: true } or { passed: false } — that other parts of your app can use to decide what to do.
Using task.status in context means the control task always runs — even when upstream tasks have failed or were skipped — which is what makes it a reliable observer.
const control = createTask({ name: "control", run: { context: [fetchUser.status], fn: (ctx) => { const passed = ctx.every((status) => status === "done")
return { ok: { passed } } }, },})
compose() .stage({ steps: [fetchUser, analytics] }) .stage({ steps: [control] }) .run() .then((scope) => scope.get(control.result)) .then(console.log)Try uncommenting the failure in fetchUser to see the control task catch it:
import { compose, createTask } from "@grlt-hub/app-compose"
// Critical — the page can't work without thisconst fetchUser = createTask({ name: "fetchUser", run: { fn: () => { // 👇 Uncomment to simulate failure // throw new Error("[fetchUser]: failed") return { ok: { id: 1, name: "Alice" } } }, },})
// Non-critical — nice to have, but the page works without itconst analytics = createTask({ name: "analytics", run: { fn: () => { throw new Error("service unavailable") }, },})
// Control task:// observes critical tasks and decides if the app start succeededconst control = createTask({ name: "control", run: { context: [fetchUser.status], fn: (ctx) => { const passed = ctx.every((status) => status === "done")
return { ok: { passed } } }, },})
compose() .stage({ steps: [fetchUser, analytics] }) .stage({ steps: [control] }) .run() .then((scope) => scope.get(control.result)) .then(console.log)