Je suis un débutant dans l'environnement node.js. J'ai lu beaucoup de sources sur la mise en œuvre des promesses et leur enchaînement. J'essaie d'éviter la mise en œuvre anti-modèle, mais je n'ai pas pu comprendre comment puis-je le faire.

Il existe un flux d'enregistrement d'utilisateurs dans le système. Tout d'abord, je vérifie le nom d'utilisateur. s'il n'y a pas d'utilisateur dans la base de données avec ce nom d'utilisateur, je crée un modèle utilisateur et l'enregistre dans la base de données.

Pouvez-vous bien vouloir voir mon commentaire en ligne?

App.js

RegisterUser("existUser","123").then(user=>{
    //send response, etc
}).catch(er => {
    console.log(er);
    //log error,send proper response, etc
    // Should I catch error here or inner scope where RegisterUser implemented ?
});

UserService.js

function RegisterUser(username, password) {
    return new Promise((resolve, reject) => {
        GetUser(username) 
            .then(user=>{
                if(user)reject(new Error("User Exists"));
                else{
                    resolve(SaveUser(username,password))// Is that ugly?
                                                        //what if I have one more repository logic here ?
                                                        //callback train... 
                }
            }) 
            .then(user => {
                resolve(user);//If I do not want to resolve anything what is the best practice for it, like void functions?
            }).catch(err=>{
                console.log(err); // I catch the db error what will I do now :)
                reject(err);// It seems not good way to handle it, isn't it ?
                // Upper promise will handle that too. But I dont know how can I deal with that.
            });;
    });
}

Repository.js

function GetUser(username) {
    return new Promise((resolve, reject) => {
        if (username === "existUser")
            resolve("existUser");
        else resolve("");
    });
}

function SaveUser(username, password) {
    return new Promise((resolve, reject) => {
        reject(new Error("There is a problem with db"));//assume we forgot to run mongod 
    });
}

Le code ci-dessus me semble affreux. J'ai pensé que je devais définir une méthode qui peut enchaîner après la méthode GetUser. comme

GetUser(username)
.then(SaveUserRefined)// how could it know other parameters like password, etc
.then(user=> {resolve(user)}) // resolve inside then ? confusing.
.catch(er=>//...);

Je sens que je fais de l'anti-pattern ici et que je crée "l'enfer des promesses". Comment un simple flux comme celui-ci pourrait-il être implémenté? Validez le nom d'utilisateur et enregistrez-le.

Merci.

2
Ozan Ertürk 15 avril 2018 à 20:44

3 réponses

Meilleure réponse

Si vous travaillez déjà avec des promesses, il n'est pas nécessaire de créer les vôtres. Lorsque vous appelez .then sur une promesse et fournissez une fonction indiquant quoi faire, cela crée une nouvelle promesse, qui résoudra tout ce que la fonction retourne.

Votre fonction registerUser devrait donc ressembler à ceci:

function registerUser(username, password) {
  return getUser(username)
    .then(user => {
      if (user) {
        throw new Error('User Exists');
      }
      return saveUser(username, password)
    });
}

Et vous l'utilisez comme ceci:

registerUser('starfox', 'doABarrelRoll')
  .catch(error => console.log(error);

Notez que si SaveUser provoque une erreur, il se retrouvera dans ce dernier .catch, car je n'ai pas mis de gestion d'erreur dans registerUser. C'est à vous de décider où vous souhaitez gérer les erreurs. Si c'est quelque chose de récupérable, peut-être que registerUser peut le gérer, et le monde extérieur n'a jamais besoin de le savoir. Mais vous lancez déjà une erreur si le nom d'utilisateur existe, donc l'appelant devra être conscient des erreurs de toute façon.


De plus, vos fonctions getUser et saveUser peuvent également éviter de créer leurs propres promesses, en supposant que l'implémentation réelle appelle une fonction qui renvoie une promesse.

3
Nicholas Tower 15 avril 2018 à 18:05

Vous devez utiliser la syntaxe async/await pour éviter Promise Hell.

Changez votre code comme ceci

/**
 * This is a promise that resolve a user or reject if GetUser or SaveUser reject
 */
async function RegisterUser (username, password) { 
    var user = await GetUser(username)

    if (user)
        return user;

    var userSaved = await SaveUser(username,password)
    return userSaved
}

Si vous utilisez RegisterUser dans un async function code juste

async function foo () {
    try {
        var usr = await RegisterUser('carlos', 'secret123')
        return usr
    } catch (e) {
        console.error('some error', e)
        return null
    }
}

Ou si vous l'utilisez comme une promesse

RegisterUser('carlos', 'secret123')
    .then(usr => console.log('goood', usr))
    .catch(e => console.error('some error', e))
0
Fernando Carvajal 6 déc. 2018 à 17:39

Oui, c'est le {{X0} } antipattern du constructeur! Ecrivez

function registerUser(username, password) {
    return getUser(username).then(user => {
        if (user) throw new Error("User Exists");
        else return saveUser(username,password)
    });
}

Remarquez que j'ai également mis en minuscule les noms de vos fonctions. Vous ne devez capitaliser que vos fonctions constructeur.

Dois-je intercepter une erreur ici ou une portée interne où registerUser est implémenté?

Vous devez détecter les erreurs là où vous pouvez les gérer - et vous devez gérer les erreurs à la fin de la chaîne (en les enregistrant, généralement). Si registerUser ne fournit pas de résultat de secours, il n'a pas besoin de gérer quoi que ce soit, et il n'a généralement pas besoin non plus de consigner le message d'erreur seul (si vous le souhaitez, voir ici pour un exemple).

Voir aussi Ai-je toujours besoin de catch () à la fin même si j'utilise un rappel de rejet dans tous les cas?.

4
Bergi 15 avril 2018 à 18:03