Aprende leyendo en orden

Capturar excepciones — try / catch / finally

Captura excepciones con try y catch, deja la limpieza en finally, relanza con throw y separa los casos según el tipo de error.

Cuando usas JSON.parse para convertir de nuevo en objeto una cadena que recibiste, basta un solo carácter mal puesto para que la ejecución se detenga en esa línea, y el código que muestra y guarda los datos, escrito más abajo, nunca llega a ejecutarse. Tampoco puedes descartar de antemano todas las causas posibles con comprobaciones if.

En este artículo verás try / catch, que captura la excepción que detuvo tu código, y finally, que se ejecuta siempre.

Capturar una excepción — try y catch

Imagina que quieres leer una cadena de configuración que llega del servidor y aplicarla a la página. Si la cadena está mal formada, JSON.parse lanza una excepción (un error que interrumpe la ejecución) en esa línea, y ninguna de las líneas de abajo se ejecuta. La página se queda en blanco y no pasa nada más.

Si se lanza una excepción dentro de try (un bloque que envuelve código que puede fallar), la ejecución salta a catch (un bloque que recibe la excepción). El error de catch (error) es solo el nombre que le das al valor recibido (funciona igual con err o con cualquier otro nombre). Contiene name, el nombre del tipo de error, y message, una descripción de lo que salió mal.

// Una cadena a la que le sobra una coma al final
const configText = '{"theme":"dark","fontSize":14,}';

try {
  const config = JSON.parse(configText);
  console.log(config.theme);          // esta línea no se ejecuta
} catch (error) {
  // si se lanza una excepción, la ejecución salta aquí
  console.log("No se pudo cargar la configuración");  // No se pudo cargar la configuración
  console.log(error.name);                             // SyntaxError
}

// como hay un catch, las líneas siguientes se ejecutan
console.log("Se muestra la configuración predeterminada");  // Se muestra la configuración predeterminada
Sin try y con try
parse sintryExcepción enesa líneaNo hay adóndesaltarLas siguientesno se ejecutanparse dentrode tryExcepción enesa líneaSalta acatch (error)Se ejecutan laslíneas tras try
En los dos casos, la línea que va justo debajo de la que falló no se ejecuta. Con try, el control pasa a catch y sigue con las líneas que hay después de try.

Una vez que la ejecución pasa a catch, nunca vuelve para terminar la línea que falló, así que el mensaje alternativo o el valor predeterminado van dentro de catch. El texto de message cambia según el navegador; por eso, cuando necesites distinguir tipos de error, fíjate en name.

Recupera el título de un borrador guardado. savedDraft y brokenDraft ya están declarados.

① En restoreTitle, convierte la cadena en objeto y devuelve su título.

② Si falla, muestra el nombre del tipo de error y devuelve "Borrador sin título".

③ Llámala con las dos cadenas y muestra cada resultado con la forma "Título: Informe de septiembre".

(Si se ejecuta correctamente, aparecerá una explicación.)

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Ejecutar código tanto si sale bien como si falla — finally

Tareas de limpieza como ocultar un indicador de carga o cerrar una conexión abierta hacen falta tanto si la operación sale bien como si falla. Si escribes la misma línea en try y en catch, es fácil que más adelante corrijas una copia y te olvides de la otra, y los dos caminos dejan de comportarse igual.

finally (un bloque que se ejecuta siempre al salir de try) va después de catch. Tanto si try llegó hasta el final como si catch recibió una excepción, se ejecuta siempre justo antes de salir. También puedes omitir catch y escribir solo try y finally.

function loadReport(text) {
  try {
    const report = JSON.parse(text);
    console.log(report.title);
  } catch (error) {
    console.log("No se pudo cargar");
  } finally {
    // se ejecuta siempre al salir de try
    console.log("Indicador de carga oculto");
  }
}

// Si sale bien
loadReport('{"title":"Ventas de septiembre"}');  // Ventas de septiembre → Indicador de carga oculto

// Si falla
loadReport('{"title":}');                        // No se pudo cargar → Indicador de carga oculto
Dos caminos que terminan en el mismo sitio
Pasar una cadenaválidatry se ejecutahasta el finalcatch sesaltafinally seejecutaPasar una cadenamal formadaExcepción amitad de trycatch larecibefinally seejecuta
La fila de arriba y la de abajo siguen rutas distintas, pero acaban en el mismo bloque. finally se ejecuta tanto si el código sale bien como si falla.

finally se ejecuta cuando termina el código de try o de catch. Por eso, en el código de arriba, el mensaje de limpieza aparece después del título si todo sale bien, y después del mensaje de error si algo falla.

Las variables declaradas dentro de try no se pueden leer fuera

Una variable declarada con const o let dentro de try solo se puede usar dentro de ese bloque. Si la lees desde catch, desde finally o desde cualquier línea que venga después de la sentencia try, obtienes un ReferenceError. Declara con let antes de try los valores que vayas a necesitar después, y dentro de try limítate a asignarles un valor.

Importa datos de socios fila por fila y muestra el progreso. rows y processed ya están declarados, y la fila 2 está mal formada.

① Convierte cada fila en objeto y muéstrala con la forma "Ana: plan anual".

② Si falla la conversión, muestra "Hay una fila que no se pudo importar".

③ Tanto si sale bien como si falla, suma 1 a processed y muéstralo con la forma "Procesadas: 1 / 3".

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Avisar a quien llama — throw dentro de catch

Imagina que una función que lee datos de reservas captura una excepción y se limita a registrarla. La función termina con normalidad, así que quien la llama no se entera del fallo, sigue usando el valor devuelto y vuelve a detenerse en otra línea, lejos de la causa real.

Si escribes throw (una sentencia que lanza una excepción) seguido del mismo error que recibió catch, esa excepción llega tal cual a quien llama. Así la función puede registrar el fallo por su cuenta y dejar que quien llama decida cómo responder.

function readEntry(text) {
  try {
    return JSON.parse(text);
  } catch (error) {
    console.log(`No se pueden leer los datos de la reserva: ${error.name}`);
    throw error;   // relanza tal cual la excepción recibida
  }
}

try {
  const entry = readEntry('{"name":"Carlos","seats":}');
  console.log(entry.name);              // esta línea no se ejecuta
} catch (error) {
  console.log("Reserva cancelada");     // Reserva cancelada
}
Relanzar o no relanzar
catch conthrow errorEl SyntaxErrorsube hacia fueraEl catch externolo recibe"Reservacancelada"catch sinthrow errorEl valor devueltoes undefinedTypeError enentry.nameLlega otro errordistinto
En la fila de abajo, la excepción se queda dentro de la función y el código vuelve a fallar en la línea que intenta leer una propiedad de undefined. Si relanzas, el SyntaxError original llega a quien llama.

Si quien llama también necesita reaccionar a una excepción capturada dentro de una función, relánzala escribiendo throw error; al final de catch. Las líneas que van después de throw no se ejecutan, así que pon antes la línea que registra el fallo.

Un catch vacío hace desaparecer el fallo

Si dejas vacío catch (error) { }, la excepción parece esfumarse, pero el fallo no queda registrado en ningún sitio y luego no puedes rastrear la causa. Lo más habitual es que la página se quede en blanco sin más. Haz siempre al menos una de estas cosas: mostrar un mensaje, recurrir a un valor predeterminado o relanzar.

Carga un diseño guardado. savedLayout, layout y errorLog ya están declarados.

① En loadLayout, convierte la cadena en objeto y devuelve el resultado.

② Si falla, añade a errorLog el nombre del tipo de error y relanza la excepción.

③ Asigna el resultado a layout y, si falla, muestra "Se muestra el diseño predeterminado".

④ Muestra el número de columnas y el registro con la forma "Columnas: 2" y "Registro: SyntaxError".

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado

Separar por tipo de error — instanceof y name

catch recibe todas las excepciones, sea cual sea su tipo. Pero el mensaje que quieres mostrar a los usuarios cambia según si los datos estaban mal formados o si faltaba un valor que intentabas leer.

instanceof (un operador que comprueba si el valor de la izquierda se creó a partir del tipo de la derecha) te permite comprobar el tipo, como en error instanceof SyntaxError. error.name también contiene el nombre del tipo como cadena.

const applicant = { name: "Ana", contact: null };

try {
  console.log(applicant.contact.email);
} catch (error) {
  if (error instanceof SyntaxError) {
    console.log("El formato de los datos no es válido");   // aquí no llega
  } else if (error instanceof TypeError) {
    console.log("Falta el valor que intentaste leer");      // Falta el valor que intentaste leer
  } else {
    console.log(`Tipo de error inesperado: ${error.name}`);
  }

  console.log(error.name);                       // TypeError
}
Tres excepciones, un solo catch
Excepción lanzadadentro de tryparse de un JSONmal formadoLeer una propiedadde nullPedir a toFixed200 dígitosname esSyntaxErrorname esTypeErrorname esRangeErrorAvisar de formatono válidoAvisar de quefalta el valorRegistrarla comoinesperada
Las tres llegan al mismo catch (error), pero cada una tiene un name distinto. Si compruebas el tipo, puedes mostrar mensajes distintos.

Las excepciones que no encajan con ninguna comprobación las recoge todas el else final. La tabla de abajo muestra cuatro tipos con los que te encontrarás a menudo y cuándo se lanza cada uno.

TipoCuándo se lanzaCódigo de ejemplo que lo lanza
SyntaxErrorLa cadena no tiene formato JSONJSON.parse("{,}")
TypeErrorLeíste una propiedad de null o undefinedapplicant.contact.email
RangeErrorPasaste un valor fuera del rango permitido, como un número de dígitos(1980).toFixed(200) (el máximo es 100)
ReferenceErrorLeíste un nombre que nunca se declaróconsole.log(total)

Unos sensores de temperatura envían sus lecturas como cadenas; procésalas. payloads ya está declarado, y dos de las tres lecturas tienen problemas.

① Convierte cada una en objeto y muéstrala con la forma "T-1: 21.5 grados", con la temperatura redondeada a 1 decimal.

② Si el formato está roto, muestra "El formato de los datos recibidos no es válido".

③ Si no hay temperatura, muestra "No hay lectura de temperatura".

④ En cualquier otro caso, muestra "Inesperado: " seguido del nombre del tipo de error.

Editor JavaScript / TypeScript

Ejecutar el código para ver el resultado
QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1¿Qué muestra try { console.log(1); } finally { console.log(2); }?

Pregunta 2Si el catch de una función solo registra el error y no incluye throw error;, ¿qué valor recibe quien la llama?

Pregunta 3¿Qué operador usas para comprobar el tipo del error que recibió catch?