Home › Learn › JavaScript › Async, errors, and classes

Errors: throw, try, catch

Lesson 38 of 40. Refuse bad input loudly and recover from failures.

throw new Error("message") stops the current function and hands an error up to whoever called it. Throwing is the right response to input a function cannot do anything sensible with; a made-up return value hides the problem until it surfaces somewhere confusing. try { ... } catch (e) { ... } catches errors thrown inside the block (e.message has the text); finally runs either way.

Subclass Error when callers need to tell your error apart: class ValidationError extends Error {} and then e instanceof ValidationError. Do not catch errors you cannot handle; let them reach code that can.

function sqrt(x) {
  if (x < 0) throw new RangeError("negative input");
  return Math.sqrt(x);
}
try {
  sqrt(-1);
} catch (e) {
  console.log(e.name, e.message);   // RangeError negative input
}

Your task

Write withdraw(balance, amount) that throws an Error with the message "amount must be positive" when the amount is zero or less, throws "insufficient funds" when the amount is more than the balance, and otherwise returns the new balance. Then write tryWithdraw(balance, amount) returning { ok: true, balance } on success or { ok: false, error } with the error message on failure.

Finally, validateOrder(order): throw "no items" when order.items is not an array or is empty, throw bad quantity for <name> for the first item whose qty is not a positive whole number, and otherwise return the order unchanged.

Part of the final project. validateOrder refuses an order with no items or a bad quantity before any money is worked out. It is saved in this browser and loaded into Project: checkout when you get there.

Hint 1

if (amount <= 0) throw new Error("amount must be positive");

Hint 2

tryWithdraw: try { return { ok: true, balance: withdraw(balance, amount) }; } catch (e) { return { ok: false, error: e.message }; }. validateOrder: if (!Array.isArray(order.items) || order.items.length === 0) throw new Error("no items"); then loop: if (!Number.isInteger(item.qty) || item.qty <= 0) throw new Error("bad quantity for " + item.name);

Show the solution
function withdraw(balance, amount) {
  if (amount <= 0) throw new Error("amount must be positive");
  if (amount > balance) throw new Error("insufficient funds");
  return balance - amount;
}

function tryWithdraw(balance, amount) {
  try {
    return { ok: true, balance: withdraw(balance, amount) };
  } catch (e) {
    return { ok: false, error: e.message };
  }
}

function validateOrder(order) {
  if (!Array.isArray(order.items) || order.items.length === 0) throw new Error("no items");
  for (const item of order.items) {
    if (!Number.isInteger(item.qty) || item.qty <= 0) throw new Error("bad quantity for " + item.name);
  }
  return order;
}

    Runs in an isolated web worker in this tab; nothing is sent anywhere.