Fastify中不使用async/await或.catch时如何处理UnhandledPromiseRejectionWarning
Fastify 捕获未处理异步错误触发全局错误处理方案
问题根因
你当前代码不生效的核心原因是Fastify的setErrorHandler默认仅能捕获两类错误:
- 路由处理函数同步执行时抛出的异常
- 路由处理函数返回的Promise被reject时抛出的异常
你代码里的fetch返回的Promise没有被return到路由处理函数外层,也没有绑定catch处理,属于游离的未托管Promise,Fastify的错误处理链感知不到这部分异常,所以会触发Node.js全局的UnhandledPromiseRejectionWarning,同时请求因为一直未调用response.send会超时挂起。
解决方案
方案1:统一封装请求库(符合你允许修改请求库的前提)
通过Fastify钩子给每个请求上下文注入封装后的请求方法,内部自动兜底错误,无需开发者手动加catch:
import fastify from 'fastify'; import originalFetch from 'node-fetch'; const app = fastify(); app.setErrorHandler(async (error, request, response) => { console.log('setErrorHandler'); response.status(500).send('error'); }); // 全局注入封装后的fetch app.addHook('onRequest', (request, reply, done) => { request.fetch = (url, options) => { return originalFetch(url, options) .then(res => res) .catch(err => { // 错误自动触发全局错误处理 if (!reply.sent) reply.send(err) throw err }) } done() }) // 开发者无需修改原有写法,仅需要调用上下文中的fetch即可 app.get('/', (request, response) => { request.fetch('https://...').then(() => { response.send(); }); });
方案2:全局兜底未处理的Promise rejection(无需修改开发者业务代码)
如果无法约束开发者统一使用封装后的请求库,可以结合Node.js的AsyncLocalStorage跟踪请求上下文,全局捕获所有未处理的异步错误,自动关联到对应请求触发错误返回:
import fastify from 'fastify'; import fetch from 'node-fetch'; import { AsyncLocalStorage } from 'async_hooks' const app = fastify(); const asyncStorage = new AsyncLocalStorage() app.setErrorHandler(async (error, request, response) => { console.log('setErrorHandler'); response.status(500).send('error'); }); // 请求进入时存入异步上下文 app.addHook('onRequest', (request, reply, done) => { asyncStorage.run({ request, reply }, done) }) // 全局监听未处理的Promise rejection process.on('unhandledRejection', (reason) => { const store = asyncStorage.getStore() // 仅处理未返回响应的当前请求关联的错误 if (store && store.reply && !store.reply.sent) { store.reply.send(reason) return } // 非请求关联的错误正常兜底打日志 console.error('全局未处理Promise rejection:', reason) }) // 开发者的业务代码不需要做任何修改 app.get('/', (request, response) => { fetch('https://...').then(() => { response.send(); }); });
该方案完全不需要修改开发者编写的接口处理逻辑,无论开发者是否忘记加catch、是否使用async/await,都能自动捕获错误触发全局错误处理,返回500状态码。
内容的提问来源于stack exchange,提问作者Rodrigo Ruiz
相关产品推荐
相关产品推荐

