A caller may need to know which task failed as well as why it failed. Extend Error to introduce a recognizable TaskError carrying an index and the original cause. The second Error constructor argument accepts an options object with cause in the environments used by this course.
Keep messages useful for people and explicit properties useful for code. Do not ask callers to parse an index back out of an English message. instanceof TaskError works for instances from this class in the same realm; serializing an error to JSON or crossing a realm does not preserve every identity assumption.
A thrown value may be undefined, null or text. Store an explicit failed flag instead of testing whether the stored error is truthy. Likewise, preserve the first observed failure rather than overwriting it every time another cancelled callback rejects. This policy is about observation order, not guaranteeing which physical operation failed first.
Read the subclass constructor
class TaskError extends Error makes TaskError a subclass of Error: an instance is also an Error. Inside its constructor, super(message, options) calls the Error constructor to initialize the message and cause. Call it before accessing this; then add the task-specific index and name. Here cause is the original failure value, not necessarily another Error.
Follow the example in three steps: initialize the Error part with super, store the index on this instance, then inspect the instance in the caller. You do not need a general inheritance hierarchy to use this one specialized error. See the extends reference for the language rule.
Predict, run, and explain
Write the output and trace the important state changes before checking it. Then run the program. Each editor is independent. Async examples end with main() so this editor waits for their work.
class TaskError extends Error {
constructor(index, cause) {
super(`Task ${index} failed`, { cause });
this.name = "TaskError";
this.index = index;
}
}
const error = new TaskError(2, new Error("offline"));
console.log(error instanceof Error);
console.log(error.index, error.cause.message);Check your prediction
true
2 offline Preserve machine-readable failure information
An error message helps a person, while explicit properties let code inspect a failure without parsing English text. Preserve an original cause rather than replacing it with an unrelated message. A task index and cause describe a different failure boundary from an input iterator error.
Not every thrown value is an Error instance. Undefined and null are still legal failures. Track whether failure occurred with a separate flag so a falsy reason cannot accidentally turn rejection into success.
class TaskError extends Error {
constructor(index, cause) {
super(`Task ${index} failed`, { cause });
this.index = index;
}
}
const original = new Error("offline");
const wrapped = new TaskError(2, original);
console.log(wrapped.index);
console.log(wrapped.cause === original);
console.log(wrapped instanceof Error);Check your prediction
2
true
true The original object survives wrapping by identity. Callers can inspect cause and index separately; changing message wording should not break those callers.
let failed = false;
let reason;
try {
throw undefined;
} catch (error) {
failed = true;
reason = error;
}
console.log(failed);
console.log(Boolean(reason));Check your prediction
true
false A truthiness check on reason would misclassify the outcome. This is why the final pool keeps a failed flag independently of its stored failure value.
Try the rule on new inputs
Test an Error, text, null and undefined as causes. For mapper failure versus source failure, state exactly which one is wrapped and which original value the caller should receive.
Your turn
Error Types & Causes: practise
Implement TaskError with index and cause. Wrap a string cause “bad input” at index 3. Print TaskError, 3 and bad input. Do not convert the cause into a new unrelated message.
// Extend Error and set a stable name for readable diagnostics.Hint 1: the idea
The cause may be any value, not just an Error instance.
Hint 2: a first step
Pass { cause } to super and save index on the instance.
Show one solution
class TaskError extends Error {
constructor(index, cause) {
super(`Task ${index} failed`, { cause });
this.name = "TaskError";
this.index = index;
}
}
const error = new TaskError(3, "bad input");
console.log(error.name);
console.log(error.index);
console.log(error.cause);TaskError
3
bad inputExplain why the implementation meets the stated contract. Which part is enforced by the language or checker, and which part needs behavioral evidence?
Change it and test it
Test undefined as a cause. Create a separate failed flag and demonstrate that throwing undefined still sets it. Explain why testing if(error) cannot represent every thrown value and why a cleanup error should not erase the primary failure.
Completion is saved in this browser only. It does not measure mastery.