为了账号安全,请及时绑定邮箱和手机立即绑定

Express 中间件中的异步/等待

Express 中间件中的异步/等待

PIPIONE 2022-06-16 17:31:53
我无法理解如何在 Express 中正确编写使用 async/await 的中间件,但在执行后不会让 Promise 漂浮在以太中。我已经阅读了大量的博客和 StackOverflow 帖子,似乎对于在 async/await 中间件中使用以下模式达成了一些共识:const asyncHandler = fn => (req, res, next) =>  Promise    .resolve(fn(req, res, next))    .catch(next)app.use(asyncHandler(async (req, res, next) => {  req.user = await User.findUser(req.body.id);  next();}));我知道这使得不必在所有 aysnc 路由处理程序中使用 try..catch 逻辑成为可能,并确保 (async (req, res, next) => {}) 返回的 Promise函数已解决,但我的问题是我们从 asyncHandler 的 Promise.resolve() 调用中返回了一个 Promise:Promise  .resolve(fn(req, res, next))  .catch(next)并且永远不要在这个返回的 Promise 上调用 then()。使用这种模式是因为我们不依赖中间件函数的任何返回值吗?是否可以只返回 Promises 而从不调用 then() 来获取它们的值,因为 Express 中的中间件没有返回有意义的值?我知道 async/await 允许我们处理异步代码并轻松处理返回的值,但在 Express 中间件中,我们留下了顶级异步,它解析为 Promise,然后我们使用 Promise.resolve 解析(),但它仍然解析为 Promise...另外,我知道这个问题有 3rd 方解决方案,你可以使用另一个框架,比如 Koa。我只是想了解如何在 Express 中正确执行此操作,因为我对使用 Node 进行后端开发还比较陌生,并且希望只专注于 Express,直到我掌握了基础知识。我暂定的解决方案是仅在非中间件函数中使用 async/await,然后在实际中间件中对返回的 Promises 调用 then() ,这样我就可以确定我没有做任何顽皮的事情,例如:app.use((req, res, next) => {  User.findUser(req.body.id)    .then(user => {      req.user = user;      next();    })    .catch(next)});这对我来说很好,但我一直在到处看到 asyncWrapper 代码。我想太多了对吧?
查看完整描述

3 回答

?
HUWWW

TA贡献1874条经验 获得超12个赞

并且永远不要在这个返回的 Promise 上调用 then()。使用这种模式是因为我们不依赖中间件函数的任何返回值吗?


是否可以只返回 Promises 而从不调用 then() 来获取它们的值,因为 Express 中的中间件没有返回有意义的值?


是的,如果您要跟踪的只是它是否被拒绝,因为它处理自己的成功完成,但您需要单独处理错误,那么您可以使用.catch()这实际上是您正在做的事情。这可以。


如果我经常这样做,我会切换到像 Koa 这样的承诺友好型框架,或者我会添加自己的承诺感知中间件注册。例如,下面是 Express 的一个附加组件,它为您提供了 Promise-aware 中间件:


// promise aware middleware registration

// supports optional path and 1 or more middleware functions

app.useP = function(...args) {

    function wrap(fn) {

        return async function(req, res, next) {

            // catch both synchronous exceptions and asynchronous rejections

            try {

                await fn(req, res, next);

            } catch(e) {

                next(e);

            }

        }

    }

    

    // reconstruct arguments with wrapped functions

    let newArgs = args.map(arg => {

        if (typeof arg === "function") {

            return wrap(arg);

        } else {

            return arg;

        }

    });

    // register actual middleware with wrapped functions

    app.use(...newArgs);

}

然后,要使用这个可感知承诺的中间件注册,您只需像这样注册它:


app.useP(async (req, res, next) => {

  req.user = await User.findUser(req.body.id);

  next();

});

而且,任何被拒绝的承诺都会自动为您处理。


这是一个更高级的实现。把它放在一个名为的文件中express-p.js:


const express = require('express');


// promise-aware handler substitute

function handleP(verb) {

    return function (...args) {

        function wrap(fn) {

            return async function(req, res, next) {

                // catch both synchronous exceptions and asynchronous rejections

                try {

                    await fn(req, res, next);

                } catch(e) {

                    next(e);

                }

            }

        }


        // reconstruct arguments with wrapped functions

        let newArgs = args.map(arg => {

            if (typeof arg === "function") {

                return wrap(arg);

            } else {

                return arg;

            }

        });

        // register actual middleware with wrapped functions

        this[verb](...newArgs);

    }

}


// modify prototypes for app and router

// to add useP, allP, getP, postP, optionsP, deleteP variants

["use", "all", "get", "post", "options", "delete"].forEach(verb => {

    let handler = handleP(verb);

    express.Router[verb + "P"] = handler;

    express.application[verb + "P"] = handler;

});


module.exports = express;

然后,在你的项目中,而不是这个:


const express = require('express');

app.get(somePath, someFunc);

用这个:


const express = require('./express-p.js');

app.getP(somePath, someFunc);

然后,您可以自由使用这些方法中的任何一种,它们会自动处理从路由返回的被拒绝的 Promise:


 .useP()

 .allP()

 .getP()

 .postP()

 .deleteP()

 .optionsP()

在您创建的应用程序对象或您创建的任何路由器对象上。此代码修改原型,因此您在加载此模块后创建的任何应用程序对象或路由器对象将自动具有所有这些承诺感知方法。


查看完整回答
反对 回复 2022-06-16
?
慕勒3428872

TA贡献1848条经验 获得超6个赞

你在做什么是绝对好的。但对于那些想得太多的人来说,有一个简单的解决方案。只需重新编写asyncHandler.


const asyncHandler = fn => (req, res, next) => {

     fn(req, res, next)

     .catch(next);

}                                  

     

我们不需要Promise.resolve()在asyncHandler. 因为fn是一个async函数,所以它返回一个承诺。catch()如果函数内部有错误,我们就可以承诺。


在这里我们没有从asyncHandler函数返回任何东西,因为我们不需要。


查看完整回答
反对 回复 2022-06-16
?
开心每一天1111

TA贡献1836条经验 获得超13个赞

您可以为此使用 lib express-async-errors。它修补express没有问题。



查看完整回答
反对 回复 2022-06-16
  • 3 回答
  • 0 关注
  • 221 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信