web-dev-qa-db-fra.com

Utilisation de boucles et de promesses dans les transactions dans Sequelize

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);
        });
13
ReturnToZero

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. 

27
Datsik

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
5

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;
    }
  }
}
1
Ryan

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);
      }
    });
  });
}
0
Eri Jonhson