1
votes

Enregistrer la valeur de then dans une variable en dehors de la promesse

Je suis assez nouveau dans le concept des promesses et j'essaye de comprendre comment fonctionnent les portées. J'essaie essentiellement de stocker la valeur de l'intérieur d'un then () dans une variable en dehors de Promise

Voici une fonction simple que j'ai écrite dans Nodejs (Express), en utilisant Sequelize pour exécuter la requête sur la base de données.

exports.getTest = (req, res, next) => {
    var categories = [];
    var names = ['Category 1', 'Category 2', 'Category 3', 'Category 4'];
    for (var i = 0; i < names.length; i++) {
        model.Category.findOne({
            where: {
                name: names[i]
            },
            attributes: ['id']
        }).then(id => {
            categories.push(
            {
                category_id: id.id
            });
        });
    }
    res.json(categories);
}

J'ai une autre logique à exécuter après cela, et j'ai une boucle for autour de la promesse. Donc, je ne peux pas exécuter ma prochaine logique à l'intérieur du then, sinon je la ferai exécuter plusieurs fois à cause de la boucle for. Je dois renseigner le tableau categories pour l'utiliser dans ma prochaine opération.

Actuellement, ma réponse ( res.json (categories) ) est []

Toute aide serait appréciée.

PS: Je sais que c'est un sujet courant, mais comme je l'ai mentionné, je suis assez nouveau dans ce domaine et les autres réponses ne correspondaient pas à mon scénario et me déroutaient davantage.

Merci d'avance!


1 commentaires

avez-vous essayé d'utiliser async / await?


3 Réponses :


2
votes

Dans votre cas, les catégories renverront toujours [] car vous n'attendez pas que toutes vos promesses se terminent avant de renvoyer votre réponse. Les boucles For n'attendent pas la fin des actions asynchrones avant de passer à l'itération suivante. Par conséquent, la boucle se termine et une réponse est renvoyée avant que l'une d'elles ne se termine.

Au lieu d'appeler des promesses dans une boucle for, vous devez les pousser dans un tableau, que vous pouvez ensuite passer à Promise.all () fonction.

Voici à quoi cela devrait ressembler

getTest()
  .then(data => {
    // data will be an array of promise responses
  }).catch(err => {
    console.log(err);
  })

getTest () renvoie maintenant une promesse, donc il peut être appelé comme ça

exports.getTest = () => {
    var categories = [];
    var names = ['Category 1', 'Category 2', 'Category 3', 'Category 4'];
    var promiseArray = [];
    for (var i = 0; i < names.length; i++) {
        promiseArray.push(
          model.Category.findOne({
              where: {
                  name: names[i]
              },
              attributes: ['id']
          }).then(id => {
              categories.push(
              {
                  category_id: id.id
              });
          });
        )
    }

    return Promise.all(promiseArr)
}


1 commentaires

Salut! Merci beaucoup. Cela fonctionne parfaitement bien. Bien que votre réponse soit au point, la réponse de Jimmy m'a aidé à comprendre le concept et le flux derrière lui. Avec cette compréhension, j'ai pu ensuite modifier le code en fonction de mes besoins. Mais merci beaucoup pour votre réponse!



-1
votes
Categories.findOne({where: {name: name}).then(function(category){
    // do something with that category
})

2 commentaires

ForEach peut être utilisé à peu près de la même manière, où au lieu d'écrire categories.forEach (function (category) {// faire quelque chose}) , nous utilisons la fonction de flèche et chaque catégorie de ce tableau sera passée comme argument de votre fonction


Cela ne résout pas du tout le problème, c'est à peu près le même code qu'OP, sauf qu'au lieu d'utiliser une boucle for, vous utilisez .forEach



1
votes

Vous pouvez essayer Promise.all ()

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all

La méthode Promise.all () retourne une seule Promise qui se résout lorsque toutes les promesses passées comme un itérable sont résolues ou lorsque l'itérable ne contient aucune promesse. Il rejette avec la raison de la première promesse qui rejette.

var getTest = (req, res, next) => {
    var categories = [];
    var promises = [];
    var names = ['Category 1', 'Category 2', 'Category 3', 'Category 4'];
    var resolveCount = 0;
    for (var i = 0; i < names.length; i++) {
        // Store the name in variable so that can be persistent and not
        // affected by the changing 'i' value
        const name = names[i]
        promises.push(new Promise((resolve, reject) => {
        
          // Your DB calls here. We shall use a simple timer to mimic the
          // effect
          setTimeout(() => {
            categories.push(name)
            resolveCount++;
            resolve();
          }, 1000)
        }));
    }
    
    
    Promise.all(promises).then(function() {
      console.log("This should run ONCE before AFTER promise resolved")
      console.log("resolveCount: " + resolveCount)
      console.log(categories);
      
      // Do your logic with the updated array
      // res.json(categories);
    });
    
    console.log("This will run immediately, before any promise resolve")
    console.log("resolveCount: " + resolveCount)
    console.log(categories)
}

getTest();


2 commentaires

Hé! Merci beaucoup. Cela a parfaitement fonctionné. La solution d'Isaac aussi. Cependant, vous avez encapsulé les appels DB dans une autre Promise new Promise ((résoudre, rejeter) . Et dans la solution d'Isaac, j'ajoute directement les appels DB (Promises) dans le tableau des promesses. Quelle différence Les résultats sont exactement les mêmes, cependant.


La fonction model.Category.findOne (..). Then () renvoie une Promise, vous pouvez donc simplement pousser le résultat de cette fonction dans le tableau. Pour mon exemple cependant, j'ai créé plusieurs nouvelles promesses pour illustrer l'idée générale.