HOWTO · JavaScript

Try sin Catch en JavaScript

En la publicación de hoy, aprenderemos sobre las sentencias try sin implementar catch en JavaScript.

En esta página

Sí: una sentencia try de JavaScript puede omitir catch, siempre que haya una cláusula finally presente. Este tutorial explica cuándo try sin catch es válido, qué ocurre con un error lanzado y cuándo esta forma es la herramienta adecuada.

Cómo funciona try sin catch en JavaScript

La sentencia try de JavaScript encierra código que puede lanzar un error, y es válida en cualquier lugar donde se permite una sentencia: en el nivel superior de un script, dentro de una función o anidada dentro de otro try. Lo que no puede hacer es aparecer sola: un bloque try debe ir seguido de una cláusula catch, una cláusula finally o ambas, por lo que un try aislado sin ninguna de ellas es un error de sintaxis. Las llaves {} son siempre obligatorias, incluso para una sola sentencia.

Sintaxis:

El código y los comentarios visibles de esta sección se mantienen en inglés, ya que el ejemplo se reproduce sin cambios.

try {
  // try_statements: code that may throw
} catch (exception_var) {
  // catch_statements: handle the thrown value
} finally {
  // finally_statements: always run afterward
}
  1. try_statements son las sentencias que se van a ejecutar.
  2. catch_statements son las sentencias que se ejecutan cuando se lanza un valor en el bloque try.
  3. exception_var es un identificador opcional que contiene el valor lanzado para el bloque catch asociado.
  4. finally_statements se ejecutan después de que la sentencia try finaliza, tanto si se lanzó o capturó un valor como si no.

Como catch solo es opcional cuando finally está presente (y viceversa), la gramática permite exactamente tres combinaciones:

  1. try...catch
  2. try...finally
  3. try...catch...finally

Cuando hay un bloque catch presente y una sentencia dentro del bloque try (o una función a la que este llama) lanza un valor, el control pasa inmediatamente a catch; cuando no se lanza nada, catch se omite por completo. El bloque finally siempre se ejecuta después de que los bloques try y catch terminan, tanto si se lanzó un valor como si no, y tanto si catch lo manejó como si no.

Sin cláusula catch, finally se ejecuta igualmente primero y después el valor lanzado sigue propagándose hacia el llamador. Esto también se aplica al anidamiento: las sentencias try pueden anidarse, y cuando una sentencia try interna no tiene catch, el bloque catch de un try que la envuelve maneja el error en su lugar.

Para la gramática completa, consulta la documentación de MDN sobre try...catch.

Ejemplo: try...finally sin catch

El fragmento siguiente coloca un try...finally que no tiene catch propio dentro de un try...catch externo, de modo que podemos ver el bloque finally ejecutarse y observar dónde termina el valor lanzado. 0 / 0 se evalúa como NaN, por lo que throw new Error(0 / 0) lanza un Error cuyo mensaje es NaN.

Ejecuta este ejemplo:

El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés, ya que el ejemplo se reproduce sin cambios.

try {
  try {
    console.log('Executing try block')
    throw new Error(0 / 0)
  } finally {
    console.log('Final Call')
  }
} catch (error) {
  console.log('Outer catch received: ' + error.message)
}
Executing try block
Final Call
Outer catch received: NaN

El try interno imprime su línea de registro y luego lanza. Como no tiene catch, el control pasa directamente a finally, que imprime Final Call (los mensajes de consola del ejemplo permanecen en inglés porque el código se reproduce sin cambios). Solo después de que finally termina, el Error continúa propagándose, donde el catch externo lo recibe e imprime su mensaje NaN. Sin ese catch envolvente, el mismo error llegaría al llamador o, en el nivel superior, aparecería como un error no capturado después de que se imprime Final Call.

Cuándo usar try sin catch

Recurre a try...finally cuando un bloque necesita una limpieza garantizada pero el error en sí pertenece al llamador. Las tareas típicas de finally incluyen liberar un recurso, cerrar una conexión o un descriptor de archivo, restaurar estado compartido como un botón que se vuelve a habilitar o una bandera que se restablece, y detener un temporizador: trabajo que debe ocurrir tanto si el bloque try termina normalmente como si lanza un error.

Esta forma es la opción correcta cuando el código no tiene nada significativo que añadir al error: relanzarlo sin cambios añade ruido, mientras que un bloque catch vacío se traga silenciosamente cada fallo, de modo que los errores afloran lejos de su causa. try...finally mantiene la garantía de limpieza y aún permite que el llamador vea y maneje el error. Si la función realmente necesita inspeccionar el valor lanzado, registrarlo o recuperarse, añade una cláusula catch real en su lugar.

Limitaciones y trampas de try sin catch

  • finally nunca maneja ni suprime el error. Después de que finally termina, un valor lanzado sin manejar sigue propagándose hacia el llamador; en el nivel superior termina el script o informa de un error no capturado.
  • No puedes observar el valor lanzado. Sin catch, la construcción no tiene acceso a lo que se lanzó, por lo que no puede registrar el mensaje ni ramificar según el tipo de error.
  • El flujo de control dentro de finally anula lo que estuviera pendiente. Un return o un throw dentro de finally descarta un valor que aún se estaba propagando, incluido un error lanzado, que entonces desaparece silenciosamente; por eso conviene limitar finally a una limpieza simple que ni devuelve ni lanza.
  • Las sentencias posteriores a un try...finally solo se ejecutan cuando no se lanzó nada, y el propio finally no debe depender de que el bloque try haya tenido éxito, ya que también se ejecuta en la ruta de fallo.