web-dev-qa-db-fra.com

Async / attendre dans le middleware express

J'ai du mal à comprendre comment écrire correctement le middleware en express qui utilise asynchrones/attendre, mais ne laisse pas une promesse flottante dans l'éther après son exécution. J'ai lu une tonne de blogs et de poteaux de Stackoverflow, et il semble que certains consensus sont consensus en utilisant le modèle suivant dans le middleware Async/Attendre:

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

Je comprends que cela permet de ne pas avoir à utiliser try..Catch Logic dans tous vos gestionnaires d'itinéraire AYSNC, et qu'il garantit que la promesse renvoyée par le (async (REQ, RES, Suivant) => {}). La fonction est résolue, mais mon problème est que nous retournons une promesse de la promesse d'Asynchandler.Resolve ():

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

Et ne jamais appeler alors () sur cette promesse rendue. Ce modèle est-il utilisé parce que nous ne comptons pas de valeur renvoyée des fonctions middleware? Est-ce que ça va de retourner des promesses et d'appeler ensuite () sur eux pour obtenir leur valeur, car il n'y a pas de valeur significative renvoyée du middleware dans Express?

J'obtiens que Async/attendre nous permet de gérer facilement le code ASYNC et de travailler avec les valeurs retournées, mais dans le middleware express, nous sommes laissés avec cet asynchronisation de haut niveau, qui résout une promesse, que nous résolvons ensuite avec la promesse.Resolve (), mais qui résout encore une promesse ...

De plus, je comprends qu'il y a des solutions tierces à cette question et vous pourriez simplement utiliser un autre cadre comme le KOA. Je veux juste comprendre comment procéder correctement à EXPRESS, car je suis toujours relativement nouveau dans le développement du backend avec Node et souhaitez vous concentrer uniquement sur Express jusqu'à ce que je reçois les principes fondamentaux.

Ma solution provisoire n'a été utilisée que pour les fonctions non middleware, puis appelez alors () sur les promesses retournées dans le middleware réel, afin que je puisse être sûr que je ne fais rien de méchant, comme si:

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

Ce qui va bien avec moi, mais je garde le code ASYNCWRAPPER partout sur la place. Je pense ce droit?

4

Et ne jamais appeler alors () sur cette promesse rendue. Ce modèle est-il utilisé parce que nous ne comptons pas de valeur renvoyée des fonctions middleware?

Est-ce que ça va de retourner des promesses et d'appeler ensuite () sur eux pour obtenir leur valeur, car il n'y a pas de valeur significative renvoyée du middleware dans Express?

Oui, si tout ce que vous voulez suivre, c'est si cela a été rejeté ou non parce qu'il gère sa propre achèvement réussi, mais vous devez gérer une erreur séparément, vous pouvez simplement utiliser .catch() qui est efficacement ce que vous voulez. faire faire. C'est bon.


Si je faisais beaucoup ça, je basais sur un cadre favorable à la promesse comme KOA ou j'ajouterais mon propre enregistrement middleware à la promesse. Par exemple, voici un complément à exprimer qui vous donne un middleware prometteur:

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

Ensuite, pour utiliser cet enregistrement de middleware prometteur, vous allez simplement l'enregistrer comme ceci:

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

Et toute promesse rejetée serait automatiquement manipulée pour vous.


Voici une mise en œuvre plus avancée. Mettez ceci dans un fichier appelé 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;

Ensuite, dans votre projet, au lieu de cela:

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

utilisez ceci:

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

Ensuite, vous pouvez utiliser librement l'une de ces méthodes et elles manipulent automatiquement les promesses rejetées devenues des itinéraires:

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

Sur un objet d'application, vous créez ou n'importe quel objet de routeur que vous créez. Ce code modifie les prototypes de sorte que n'importe quel objet d'application ou objet de routeur que vous créez après avoir chargé ce module aura automatiquement toutes ces méthodes de promesse.

2
jfriend00

Ce que vous faites est absolument ok. Mais pour ceux qui pensent, il y a une solution simple. Il suffit de ré-écrire le asyncHandler.

const asyncHandler = fn => (req, res, next) => {
     fn(req, res, next)
     .catch(next);
}                                  
     

Nous n'avons pas besoin d'utiliser Promise.resolve() sur le asyncHandler. Depuis fn est une fonction async, elle renvoie une promesse. Nous ne pouvons que catch() qui promettons s'il existe une erreur dans la fonction.

Et ici, nous ne retournons rien de asyncHandler fonction puisque nous n'avons pas besoin de.

0
S.Nakib