【问题标题】:Using loops and promises in transactions in Sequelize在 Sequelize 的事务中使用循环和承诺
【发布时间】:2021-04-08 05:45:15
【问题描述】:

我目前正在构建一个 Nodejs、Express、Sequelize (w. PostgreSQL) 应用程序,并且在将 Promise 与事务和循环一起使用时遇到了一些问题。

我正在尝试弄清楚如何在事务中使用 for 循环。我正在尝试遍历成员列表并在数据库中为每个成员创建一个新用户。

我知道下面的代码是错误的,但它显示了我正在尝试做的事情。

谁能指出我正确的方向?

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

【问题讨论】:

    标签: javascript node.js express promise sequelize.js


    【解决方案1】:

    你应该使用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);
        });
    

    我认为您不需要在后续交易中catch,因为我认为它会跳出交易的陷阱

    抱歉格式化。在移动。

    Promise.all 会在运行 .then 之前等待所有的 Promise 返回(或失败),并且.then 回调将是来自每个数组的所有 Promise 数据

    【讨论】:

    • 感谢您的回答,但我收到一条错误消息,告诉我需要将承诺链返回给交易。尝试在每个 Promise.all 之前添加“return”,但随后我得到 “在此事务上调用了未处理的拒绝提交(fc7be023-1980-455e-9934-7816420daa2b),您不能再使用它”
    • 是的,它缺少返回,并且没有任何东西被推送到 userPromises。
    • @BenjaminGruenbaum 谢谢我昨天在手机上做了这个,从来没有机会回来检查它,我会在我可以更新的时候更新
    • @BenjaminGruenbaum 我仍然收到Unhandled rejection commit has been called on this transaction 错误,即使返回
    【解决方案2】:

    您需要使用 sequelize 附带的 bluebird 内置循环结构:

        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(next); // convert to node err-back syntax for express
    

    【讨论】:

    • 同样有效且非常优雅的代码。但无法让.nodeify(err); 工作。它说错误未定义。以前没有使用过nodeify,所以我可能遗漏了一些东西。
    • 应该是 nodeify(next) - 抱歉
    • “PromiseConstructor”类型上不存在属性“map”。
    【解决方案3】:

    根据您对 Node.js 的实现,这可能会有所帮助。我使用 express、POSTGRES 和 sequelize 进行了相同的设置。

    我个人更喜欢 async/await (ES6) 实现而不是 then/catch,因为它更易于阅读。同时创建一个可以在外部调用的函数也提高了可重用性。

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

    【讨论】:

      【解决方案4】:

      如果有人正在寻找使用 async 和 await 的 typescript v4.0.5 的解决方案,这对我来说是可行的。也许你也可以在你的 javascript 应用程序上使用它,但这取决于它的版本。

      const array = ['one','two','three'];
      const createdTransaction = sequelize.transaction();
          
      const promises = array.map(async item => {
            await model.create({
                name: item,
            },
            { transaction: createdTransaction },
           );
      });
      
      Promise.all(promises).then(async values => {
          await createdTransaction.commit();
      });
      

      【讨论】:

        【解决方案5】:

        第一:https://caolan.github.io/async/docs.html

        所以,很容易:

        // 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);
              }
            });
          });
        }
        

        【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-09-21
        • 1970-01-01
        • 1970-01-01
        • 2021-09-07
        • 2020-05-15
        • 2018-03-25
        • 2023-03-17
        • 2017-12-18
        相关资源
        最近更新 更多