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!
3 Réponses :
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) }
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!
Categories.findOne({where: {name: name}).then(function(category){ // do something with that category })
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
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();
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.
avez-vous essayé d'utiliser async / await?