Async / Await в Express Middleware - PullRequest
1 голос
/ 07 апреля 2020

У меня проблемы с пониманием того, как правильно написать промежуточное ПО в 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();
}));

Я понимаю, что это позволяет не иметь использовать try..catch logi c во всех ваших обработчиках маршрутов aysn c и гарантировать, что Promise возвращается функцией (asyn c (req, res, next) => {}) решена, но моя проблема в том, что мы возвращаем Promise из вызова Promise.resolve () asyncHandler:

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

и никогда не вызываем then () для этого возвращенного Promise. Используется ли этот шаблон, потому что мы не полагаемся на какое-либо возвращаемое значение из функций промежуточного программного обеспечения? Можно ли просто возвращать Promises и никогда не вызывать then () для них, чтобы получить их значение, поскольку в промежуточном программном обеспечении в Express нет значимого значения?

Я понял, что async / await позволяет нам иметь дело с асиновым c кодом и легко работать с возвращаемыми значениями, но в промежуточном программном обеспечении Express мы остаемся с этим асинхронным c верхнего уровня, который разрешает Обещание, которое мы затем разрешаем с помощью Promise.resolve (), но которое по-прежнему разрешается Обещание ...

Кроме того, я понимаю, что существуют сторонние решения этой проблемы, и вы могли бы просто используйте другой фреймворк, такой как Koa. Я просто хочу понять, как сделать это правильно в Express, так как я все еще относительно новичок в бэкэнд-разработке с Node и хочу сосредоточиться исключительно на Express, пока не получу базовые знания.

Мой Предварительное решение состояло в том, чтобы использовать async / await только в функциях, не относящихся к промежуточному программному обеспечению, а затем просто вызывать then () для возвращенных Promises в реальном промежуточном программном обеспечении, чтобы я мог быть уверен, что я не делаю ничего плохого, например:

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

Что меня устраивает, но я постоянно вижу код asyncWrapper повсюду. Я слишком обдумываю это правильно?

1 Ответ

2 голосов
/ 07 апреля 2020

И никогда не вызывать then () для этого возвращенного Promise. Используется ли этот шаблон, потому что мы не полагаемся на какое-либо возвращаемое значение из функций промежуточного программного обеспечения?

Можно ли просто возвращать Promises и никогда не вызывать then () для них, чтобы получить их значение, поскольку в промежуточном программном обеспечении в Express?

да нет значимого значения , если все, что вы хотите отследить, это то, был ли он отклонен или нет, потому что он обрабатывает свое собственное успешное завершение, но вам нужно обработать ошибку отдельно, тогда вы можете просто использовать .catch(), что фактически делает то, что вы делаете. Это нормально.


Если бы я много занимался этим, я бы либо переключился на среду, обещающую обещания, такую ​​как Koa, либо добавил бы свою собственную регистрацию промежуточного программного обеспечения с учетом обещаний. Например, вот дополнение к 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');

используйте это:

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

Затем вы можете свободно использовать методы:

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

В любом объекте приложения, который вы создаете, или в любых объектах маршрутизатора, которые вы создаете. Этот код изменяет прототипы, так что любой объект приложения или объекты маршрутизатора, которые вы создаете после загрузки этого модуля, автоматически будут иметь все эти методы с учетом обещаний.

...