首页 > 解决方案 > UnhandledPromiseRejectionWarning:抛出错误时出错

问题描述

我的代码由路由、全局错误处理程序和一个验证函数组成。代码的第一个版本是完全同步的,一切正常。现在我正在尝试将其设为 asyc,因此验证函数如下所示:

const validateGenres = async (genres) => {
    const fromDB = await genre.find(); // code here is fine, it always find genres
    const validationArr = genres.reduce((acc, e) => {
        ...code
    }, [])

    if (validationArr.length !== 0) {
        throw new ValidationError(`Missing genres ${validationArr}`); //this error causes UnhandledPromiseRejectionWarning
    }

    return genres;
}

为了捕获所有错误,我创建了全局错误处理程序:

async function errorHandler(err, req, res, next) {
  logger.log({ message: err, level: 'error' });
  if (err instanceof CustomError) {
    return res.status(err.statusCode).send({ errors: err.serializeErrors() });
  }

  res.status(400).send({
    errors: [{ message: 'Something went wrong' }]
  });

}

问题是,现在每当抛出新的 ValidationError 时,它都不会去执行 errorHandler 而是会抛出:

(node:11253) UnhandledPromiseRejectionWarning: Error
    at validateGenres (/home/mat/Projects/-task-v3/-task-v3/src/middlewares/schema-validator.js:16:15)
    at async /home/mat/Projects/-task-v3/-task-v3/src/routes/findAll.js:19:28

findAll 基本上是路由器端点,如下所示:

router.get('/api/movies/findAll/:runtime?/:genres?', async (req, res) => {
    movies = await movie.find(params);  
    res.status(200).json(movies)
})

更重要的是,注册全局错误处理程序我已经这样做了:

const app = express();
app.use(json());

app.use(findAllRouter);
app.use(createRouter);

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));

app.all('*', async (req, res) => {
    res.status(404).json({ "errors": [ { "message": "No such endpoint" } ] })
});

app.use(errorHandler);


const start = () => app.listen(PORT, () => { console.log(`Listening on port ${PORT}`) });

module.exports = { start }

标签: node.jsasync-await

解决方案


async在 express 中使用函数作为中间件回调时要小心。Express 不会在async回调函数中捕获您未捕获的异常,您将获得UnhandledPromiseRejectionWarning.

如果要async回调,可以执行以下操作:

// Declare on this wrapper function
function callbackWrap(fn) {
    return (req, res, next) => {
        Promise.resolve(fn(req, res, next)).catch(next);
    };
}

// And use it in your code
router.get('/api/movies/findAll/:runtime?/:genres?', callbackWrap(async (req, res) => {
    movies = await movie.find(params);  
    res.status(200).json(movies)
}))

也为您做errorHandler(尽管如果async不需要,最好将其删除)


推荐阅读