Je suis en train de créer une application Nodejs, Express, Sequelize (w. PostgreSQL) et j'ai rencontré quelques problèmes d'utilisation de promesses avec des transactions et des boucles.
J'essaie de comprendre comment utiliser une boucle for dans une transaction. J'essaie de parcourir une liste de membres et de créer un nouvel utilisateur dans la base de données pour chacun d'eux.
Je sais que le code suivant est faux mais il montre ce que j'essaie de faire.
Est-ce que quelqu'un peut-il me montrer la bonne direction?
var members = req.body.members;
models.sequelize.transaction(function (t) {
for (var i = 0; i < members.length; i++) {
return models.User.create({'firstname':members[i], 'email':members[i], 'pending':true}, {transaction: t}).then(function(user) {
return user.addInvitations([group], {transaction: t}).then(function(){}).catch(function(err){return next(err);});
})
};
}).then(function (result) {
console.log("YAY");
}).catch(function (err) {
console.log("NO!!!");
return next(err);
});
Vous devriez utiliser un Promise.all
var members = req.body.members;
models.sequelize.transaction(function (t) {
var promises = []
for (var i = 0; i < members.length; i++) {
var newPromise = models.User.create({'firstname':members[i], 'email':members[i], 'pending':true}, {transaction: t});
promises.Push(newPromise);
};
return Promise.all(promises).then(function(users) {
var userPromises = [];
for (var i = 0; i < users.length; i++) {
userPromises.Push(users[i].addInvitations([group], {transaction: t});
}
return Promise.all(userPromises);
});
}).then(function (result) {
console.log("YAY");
}).catch(function (err) {
console.log("NO!!!");
return next(err);
});
Je ne crois pas que vous ayez besoin de catch
dans les transactions consécutives car je pense que cela saute aux yeux.
Désolé pour le formatage. Sur le mobile.
Promise.all attendra que toutes les promesses reviennent (ou échouera) avant d'exécuter le fichier .then, et le rappel .then
sera toutes les données de promesse de chaque tableau.
Vous devez utiliser les constructions en boucle intégrées de bluebird livrées avec sequelize:
var members = req.body.members;
models.sequelize.transaction(t =>
Promise.map(members, m => // create all users
models.User.create({firstname: m, email: m, 'pending':true}, {transaction: t})
).map(user => // then for each user add the invitation
user.addInvitations([group], {transaction: t}) // add invitations
)).nodeify(err); // convert to node err-back syntax for express
En fonction de votre implémentation de Node.js, cela peut aider. J'ai la même configuration en utilisant express, POSTGRES et sequelize.
Personnellement, je préférerais l'implémentation asynchrone/wait (ES6) à celle de/catch car elle est plus facile à lire. La création d'une fonction pouvant être appelée en externe améliore également la possibilité de réutilisation.
async function createMemeber(req) {
let members = req.body.members;
for (var i = 0; i < members.length; i++) {
// Must be defined inside loop but outside the try to reset for each new member;
let transaction = models.sequelize.transaction();
try {
// Start transaction block.
let user = await models.User.create({'firstname':members[i], 'email':members[i], 'pending':true}, {transaction});
await user.addInvitations([group], {transaction}));
// if successful commit the record. Else in the catch block rollback the record.
transaction.commit();
// End transaction block.
return user;
} catch (error) {
console.log("An unexpected error occurred creating user record: ", error);
transaction.rollback();
// Throw the error back to the caller and handle it there. i.e. the called express route.
throw error;
}
}
}
Premier: https://caolan.github.io/async/docs.html
Si facilement:
// requiring...
const async = require('async');
// exports...
createAllAsync: (array, transaction) => {
return new Promise((resolve, reject) => {
var results = [];
async.forEachOf(array, (elem, index, callback) => {
results.Push(models.Model.create(elem, {transaction}));
callback();
}, err => {
if (err) {
reject(err);
}
else {
resolve(results);
}
});
});
}