Tengo algunas clases que usan 'dns' de node.js. Pero cuando ocurre un error, mi aplicación se lanza. Hice un ejemplo simple con clases y funciones lanzables y me enfrenté al mismo problema. Funciona si se lanza una excepción desde la función, pero no funciona si se lanza una excepción desde la clase. Ejemplo:
class Test { constructor() { this.t(); } async t() { throw new Error("From class"); } } async function test(){ new Test(); } try { test().catch(e => { console.log("From async catch"); }); } catch (e) { console.log("From try catch"); }Producción:
Uncaught (in promise) Error: From class at Test.t (<anonymous>:6:11) at new Test (<anonymous>:3:10) at test (<anonymous>:11:3) at <anonymous>:15:3¿Cómo detectar errores del bloque try/catch en este ejemplo?
UPD: Código completo (mecanografiado):
export class RedisService { client: any; expirationTime: any; constructor(args: RedisServiceOptions) { let redisData: any = {}; if (args.password) redisData["defaults"] = { password: args.password }; dns.resolveSrv(args.host, (err, addresses) => { if (err) { /// Handling error in main func } else { log.info("Using Redis cluster mode"); redisData["rootNodes"] = addresses.map(address => { log.info(`Adding Redis cluster node: ${address.name}:${address.port}`); return Object({ url: `redis://${address.name}:${address.port}` }) }); this.client = createCluster(redisData); }; this.client.on('error', (err: Error) => log.error(`Redis error: ${err.message}`)); this.client.connect().then(() => { log.info("Connected to Redis") }); }); this.expirationTime = args.expirationTime; } /// Class functions }Generas múltiples solicitudes asíncronas pero solo puedes detectar errores desde la primera:
Creas una promesa con async function test() .
Luego, crea una llamada sincrónica dentro de él, con new Test() , cada error lanzado sincrónicamente desde dentro será capturado por catch .
Luego genera otra llamada de promesa desde dentro del constructor síncrono, este error no puede ser detectado por el bloque try/catch o .catch en o por encima de la async function test() .
Es similar a esto:
constructor() { new Promise(() => throw new Error('')) }Así que tienes 3 formas posibles de resolverlo:
async t() {} .this.t().catch(console.error) (que no se puede reenviar al bloque try/catch) ya que se reenvía al bloque catch de la Promesa detrás de la llamada asíncrona. Y si no hay .catch en la llamada asíncrona, obtiene el error "Rechazo de promesa no controlada". class Test { async t() { throw new Error("From class"); } } async function test(){ await (new Test()).t(); } try { test().catch(e => { console.log("From async catch"); }); } catch (e) { console.log("From try catch"); }No hagas métodos asíncronos)))
Su solución se parece un poco a esto.
class Test { constructor() { this.t(); } t() { (async () => { try { throw new Error("From class"); } catch (e) { console.log(e); } })(); } }Que tengas un buen resto de tu día.
no funciona si se lanza una excepción desde la clase.
En particular, cuando ocurre un evento de error asíncrono en el constructor , sí. Como dice el título de su pregunta, no puede manejar errores fuera de un contexto async , y un constructor no es eso.
Su implementación actual tiene muchos problemas, desde que client no está undefined hasta que se inicializa hasta no poder notificar a la persona que llama sobre los errores.
Todo esto se puede solucionar no poniendo código de inicialización asincrónica dentro de un constructor . Cree la instancia solo una vez que tenga todas las partes, use una función de fábrica auxiliar async para obtener (y esperar) las partes.
export class RedisService { client: RedisClient; expirationTime: number | null; constructor(client: RedisClient, expirationTime: number | null) { this.client = client; this.expirationTime = expirationTime; } static create(args: RedisServiceOptions) { const addresses = await dns.promises.resolveSrv(args.host); log.info("Using Redis cluster mode"); const redisData = { defaults: args.password ? { password: args.password } : undefined, rootNodes: addresses.map(address => { log.info(`Adding Redis cluster node: ${address.name}:${address.port}`); return { url: `redis://${address.name}:${address.port}` }; }), }; const client = createCluster(redisData); client.on('error', (err: Error) => log.error(`Redis error: ${err.message}`)); await this.client.connect(); log.info("Connected to Redis"); return new RedisService(client, args.expirationTime); } … // instance methods } Ahora, en su función principal, puede llamar a create , usar await y manejar los errores:
async function main(){ try { const service = await RedisService.create(…); } catch(e) { console.log("From async catch", e); } }