try, catch y finally en JavaScript | Nicolás Garzón
try...catch permite intentar una operación y responder de forma controlada cuando esa ejecución lanza una excepción.
JavaScript
Copiar try {
const settings = JSON . parse ( content) ;
useSettings ( settings) ;
} catch ( error) {
console. error ( "Invalid settings" , error) ;
} JavaScript
Copiar try {
riskyOperation ( ) ;
} catch ( error) {
handleError ( error) ;
} JavaScript
Copiar try {
useResource ( ) ;
} finally {
releaseResource ( ) ;
} JavaScript
Copiar try {
useResource ( ) ;
} catch ( error) {
handleError ( error) ;
} finally {
releaseResource ( ) ;
} Un try necesita al menos catch o finally.
catch recibe cualquier valor lanzado durante la ejecución síncrona del bloque try y sus llamadas descendientes.
JavaScript
Copiar function inner ( ) {
throw new Error ( "Failed" ) ;
}
try {
inner ( ) ;
} catch ( error) {
console. log ( error. message) ;
} No necesita que el throw aparezca directamente dentro del bloque.
JavaScript
Copiar try {
setTimeout ( ( ) => {
throw new Error ( "Later" ) ;
} , 0 ) ;
} catch {
} El callback se ejecuta cuando el try ya terminó.
Para promesas, el rechazo debe manejarse mediante await dentro del try o mediante .catch. Se profundizará en asincronía.
Este error impide que el archivo sea interpretado:
JavaScript
Copiar try {
const = 10 ;
} catch {
} No puede capturarse desde el mismo código porque el parser no pudo crear el programa.
JavaScript
Copiar try {
JSON . parse ( "invalid" ) ;
} catch ( error) {
error instanceof SyntaxError ;
} Aquí el programa sí es válido; la API lanza un SyntaxError durante ejecución.
JavaScript
Copiar try {
riskyOperation ( ) ;
} catch ( error) {
console. log ( error. message) ;
}
console. log ( error) ;
El binding existe únicamente dentro del bloque catch.
Cuando no necesitas el valor:
JavaScript
Copiar try {
JSON . parse ( content) ;
} catch {
return null ;
} Omitir el parámetro comunica que la decisión no depende de la causa concreta.
Aun así, descartar información debería ser una decisión consciente, no la forma automática de ocultar errores.
JavaScript
Copiar try {
throw "failed" ;
} catch ( error) {
error instanceof Error ;
} No asumas que siempre es una instancia de Error cuando consumes código externo.
JavaScript
Copiar function normalizeError ( value ) {
if ( value instanceof Error ) {
return value;
}
return new Error (
"A non-Error value was thrown" ,
{ cause : value } ,
) ;
} Dentro de tu propio código, lanzar errores reales reduce esta necesidad.
JavaScript
Copiar try {
saveOrder ( order) ;
} catch ( error) {
if ( error instanceof ValidationError ) {
showValidationMessage ( error) ;
return ;
}
throw error;
} La capa maneja el caso que comprende y deja que los fallos inesperados continúen propagándose.
Un catch no debería convertir cualquier bug en un mensaje engañoso de validación.
JavaScript
Copiar try {
const data = readInput ( ) ;
const parsed = parseInput ( data) ;
const product = createProduct ( parsed) ;
saveProduct ( product) ;
updateInterface ( product) ;
} catch ( error) {
showMessage ( "Something failed" ) ;
} No queda claro qué operación esperabas que fallara ni qué parte alcanzó a ejecutarse.
JavaScript
Copiar let parsed;
try {
parsed = parseInput ( data) ;
} catch ( error) {
return showParseError ( error) ;
}
const product = createProduct ( parsed) ;
await saveProduct ( product) ; El bloque debe rodear la operación cuyo fallo sabes interpretar.
JavaScript
Copiar function example ( ) {
try {
return "result" ;
} finally {
console. log ( "cleanup" ) ;
}
} Antes de completar el return, JavaScript ejecuta finally.
También se ejecuta cuando se lanza una excepción:
JavaScript
Copiar try {
throw new Error ( "Failed" ) ;
} finally {
closeConnection ( ) ;
} Después de la limpieza, el error continúa propagándose si finally no lo reemplaza.
JavaScript
Copiar function example ( ) {
try {
throw new Error ( "Original failure" ) ;
} finally {
return "hidden" ;
}
} El return de finally reemplaza la excepción. El consumidor recibe "hidden" y el fallo original desaparece.
También puede reemplazar otro retorno.
Evita return, break, continue o throw dentro de finally, salvo que ese reemplazo sea completamente intencional y esté muy bien justificado.
JavaScript
Copiar try {
throw new Error ( "Operation failed" ) ;
} finally {
throw new Error ( "Cleanup failed" ) ;
} El segundo error se convierte en el visible.
Cuando necesitas conservar ambos, el diseño debe registrarlos, envolverlos o utilizar mecanismos estructurados. No dejes que la limpieza borre silenciosamente la causa principal.
JavaScript
Copiar const connection = openConnection ( ) ;
try {
return connection. read ( ) ;
} finally {
connection. close ( ) ;
}
read retorna.
read lanza.
Una llamada interna lanza.
finally es apropiado para recursos cuya vida debe terminar al salir del bloque.
JavaScript
Copiar try {
try {
parseConfiguration ( content) ;
} catch ( cause) {
throw new ConfigurationError (
"Configuration is invalid" ,
{ cause } ,
) ;
}
} catch ( error) {
reportStartupFailure ( error) ;
} Cada capa cumple una responsabilidad distinta:
La interior traduce el error.
La exterior decide qué hacer con el fallo de inicio.
No anides por costumbre; hazlo cuando las fronteras de responsabilidad sean reales.
Capturar tiene sentido cuando puedes actuar.
JavaScript
Copiar try {
return readCachedProducts ( ) ;
} catch ( error) {
if ( error instanceof CacheUnavailableError ) {
return readProductsFromDatabase ( ) ;
}
throw error;
} Existe una alternativa válida. Eso es recuperación.
Capturar y retornar un array vacío para cualquier error puede convertir un fallo de permisos o de base de datos en “no existen productos”, cambiando el significado de los datos.
JavaScript
Copiar try {
return processPayment ( payment) ;
} catch ( error) {
logger. error ( "Payment processing failed" , {
paymentId : payment. id,
error,
} ) ;
throw error;
} Puede ser válido en un límite de observabilidad. Si varias capas hacen lo mismo, el mismo fallo aparecerá repetido muchas veces.
Define dónde se registra y dónde solo se añade contexto.
JavaScript
Copiar function readPreferences ( storage ) {
const rawValue = storage. getItem (
"preferences" ,
) ;
if ( rawValue === null ) {
return {
theme : "system" ,
} ;
}
try {
return JSON . parse ( rawValue) ;
} catch ( cause) {
throw new PreferencesError (
"Stored preferences are invalid" ,
{ cause } ,
) ;
}
} La ausencia es un caso normal. Un valor existente pero corrupto se traduce en un error del nivel actual.
Creer que catch solo recibe objetos Error.
Intentar capturar un error de sintaxis que impide parsear el mismo archivo.
Rodear bloques enormes sin saber qué fallo se maneja.
Capturar todo y devolver un fallback engañoso.
No relanzar errores que la capa no comprende.
Usar return dentro de finally.
Permitir que un fallo de limpieza oculte el error principal.
Registrar el mismo error en cada capa.
Esperar capturar con un try externo un callback que se ejecutará después.
catch maneja excepciones del flujo síncrono del try.
El binding de catch tiene scope propio y puede omitirse.
Cualquier valor puede ser lanzado.
Captura únicamente errores que sabes interpretar o transformar.
Mantén el bloque try tan enfocado como sea razonable.
finally se ejecuta antes de completar retorno o propagación.
Un control de flujo dentro de finally puede reemplazar el resultado original.
Capturar no equivale a recuperarse.
¿Por qué este código es peligroso?
JavaScript
Copiar function readData ( ) {
try {
return parseData ( ) ;
} finally {
return [ ] ;
}
} Respuesta El return de finally reemplaza tanto el retorno de parseData como cualquier excepción que lance. La función siempre devuelve [] y oculta el comportamiento real.
Errores personalizados explica cómo representar fallos del dominio mediante clases, códigos y datos estructurados.