DMarlon Studio

Lección 16 · 16 de 20

Errores propios y finally · JavaScript

Cree en JavaScript sus propias clases de error con extends Error, encadene la causa con cause y libere recursos siempre con finally.

JavaScript · Errores propios y finally en DMarlon Studio: el código del ejemplo y su salida en la terminal

Para distinguir unos fallos de otros conviene crear clases de error propias que hereden de Error. AppError es la base de la aplicación: llama a super(message, options) y pone en name el nombre de la clase real con this.constructor.name, de modo que cada subclase se presenta sola como ValidationError o PaymentError sin repetir código.

Las subclases pueden llevar datos extra: ValidationError guarda el campo que ha fallado en field. PaymentError no necesita nada más que su nombre, así que basta con class PaymentError extends AppError {}. El segundo argumento de Error, { cause }, encadena el error original: el rechazo del pago conserva el motivo técnico en err.cause.

En el catch, `instanceof` decide qué hacer con cada tipo de error: un aviso sobre el campo o un mensaje de pago rechazado. Los errores que no reconoce se vuelven a lanzar con throw err, para no ocultar fallos inesperados, como un error de programación.

El bloque `finally` se ejecuta siempre, tanto si try termina bien como si salta un error, e incluso si dentro hay un return. Por eso cada pedido escribe closed: es el sitio para liberar recursos, cerrar ficheros o conexiones y dejarlo todo en orden.

Código · main.js

class AppError extends Error {  constructor(message, options) {    super(message, options); // options can carry { cause }    this.name = this.constructor.name; // "ValidationError", "PaymentError"...  }}class ValidationError extends AppError {  constructor(field, message) {    super(message);    this.field = field;  }}class PaymentError extends AppError {}function charge(order) {  if (!order.email) throw new ValidationError("email", "an email is required");  if (order.amount > 500) {    throw new PaymentError(`order ${order.id} declined`, { cause: new Error("card limit is 500 EUR") });  }  return `Order ${order.id} paid: ${order.amount} EUR`;}const orders = [{ id: 1, email: "ana@shop.test", amount: 80 }, { id: 2, amount: 20 }, { id: 3, email: "luis@shop.test", amount: 900 }];for (const order of orders) {  try {    console.log(charge(order));  } catch (err) {    if (err instanceof ValidationError) console.log(`${err.name} in "${err.field}": ${err.message}`);    else if (err instanceof PaymentError) console.log(`${err.name}: ${err.message} (${err.cause.message})`);    else throw err; // unknown errors keep travelling up  } finally {    console.log(`  order ${order.id} closed`); // runs whether it failed or not  }}

Salida · node main.js

Order 1 paid: 80 EUR
  order 1 closed
ValidationError in "email": an email is required
  order 2 closed
PaymentError: order 3 declined (card limit is 500 EUR)
  order 3 closed

Para recordar

  • Las clases de error propias heredan de Error y fijan su name.
  • Un error puede llevar datos extra y su causa en { cause }.
  • instanceof distingue los tipos de error en el catch.
  • Los errores desconocidos se vuelven a lanzar.
  • finally se ejecuta siempre, haya error o no.

Ejercicio

Cree una clase StockError con una propiedad item, lánzela desde charge cuando el pedido tenga item: "lamp" y añada su caso en el catch.