快速中间件中的异步/等待 [英] Async/Await in Express Middleware

查看:105
本文介绍了快速中间件中的异步/等待的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我无法理解如何在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().使用此模式是因为我们不依赖中间件函数的任何返回值吗?因为Express中没有从中间件返回有意义的值,所以只返回Promises而不用对它们调用then()来获取它们的值是否可以?

我知道async/await允许我们处理异步代码并轻松处理返回的值,但是在Express中间件中,我们剩下的是顶级异步,它解析为Promise,然后我们将其解析为Promise.resolve(),但仍然可以解析为Promise ...

此外,我了解到存在针对此问题的第三方解决方案,您可以使用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代码.我想得太对了吗?

解决方案

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

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

是的,如果您要跟踪的只是它是否能够成功完成而被拒绝,但是您需要单独处理错误,那么您可以使用.catch(),这实际上就是您要跟踪的内容正在做.很好.


如果我经常这样做,我要么切换到像Koa这样的Promise友好框架,要么添加自己的Promise Aware中间件注册.例如,这是Express的一个附加组件,可为您提供承诺感知的中间件:

// 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);

然后,您可以自由使用以下任何一种方法,它们会自动处理从路线返回的被拒绝的承诺:

 .useP()
 .allP()
 .getP()
 .postP()
 .deleteP()
 .optionsP()

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

I'm having trouble understanding how to properly write middleware in Express that utilizes async/await, but doesn't leave a Promise floating in the ether after it's execution. I've read a ton of blogs and StackOverflow posts, and it seems like there is some consensus around using the following pattern in async/await middleware:

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();
}));

I understand that this makes it possible to not have to use try..catch logic in all of your aysnc route-handlers, and that it ensures that the Promise returned by the (async (req, res, next) => {}) function is resolved, but my issue is that we are returning a Promise from the asyncHandler's Promise.resolve() call:

Promise
  .resolve(fn(req, res, next))
  .catch(next)

And never calling then() on this returned Promise. Is this pattern used because we aren't relying on any returned value from middleware functions? Is it OK to just return Promises and never call then() on them to get their value, since there is no meaningful value returned from middleware in Express?

I get that async/await allows us to deal with the async code and work with the returned values easily, but in Express middleware we are left with that top-level async, which resolves to a Promise, which we then resolve with Promise.resolve(), but which still resolves to a Promise...

Also, I understand that there are 3rd party solutions to this issue, and you could just use another framework like Koa. I just want to understand how to do this properly in Express, as I'm still relatively new to backend development with Node and want to focus solely on Express till I get the fundamentals down.

My tentative solution has been to use async/await only in non-middleware functions, and then just call then() on the returned Promises in the actual middleware, so that I can be sure I'm not doing anything naughty, like so:

app.use((req, res, next) => {
  User.findUser(req.body.id)
    .then(user => {
      req.user = user;
      next();
    })
    .catch(next)
});

Which is fine with me, but I keep see the asyncWrapper code all over the place. I'm over-thinking this right?

解决方案

And never calling then() on this returned Promise. Is this pattern used because we aren't relying on any returned value from middleware functions?

Is it OK to just return Promises and never call then() on them to get their value, since there is no meaningful value returned from middleware in Express?

Yes, if all you want to track is whether it was rejected or not because it handles its own successful completion, but you need to handle an error separately, then you can just use .catch() which is effectively what you're doing. This is fine.


If I was doing this a lot, I'd either switch to a promise-friendly framework like Koa or I'd add-on my own promise-aware middleware registration. For example, here's an add-on to Express that gives you promise-aware middleware:

// 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);
}

Then, to use this promise-aware middleware registration, you would just register it like this:

app.useP(async (req, res, next) => {
  req.user = await User.findUser(req.body.id);
  next();
});

And, any rejected promise would automatically be handled for you.


Here's a more advanced implementation. Put this in a file called 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;

Then, in your project, instead of this:

const express = require('express');
app.get(somePath, someFunc);

use this:

const express = require('./express-p.js');
app.getP(somePath, someFunc);

Then, you can freely use any of these methods and they automatically handle rejected promises returned from routes:

 .useP()
 .allP()
 .getP()
 .postP()
 .deleteP()
 .optionsP()

On either an app object you create or any router objects you create. This code modifies the prototypes so any app object or router objects you create after you load this module will automatically have all those promise-aware methods.

这篇关于快速中间件中的异步/等待的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆