【问题标题】:Nested forEach and Asynchronity嵌套 forEach 和异步
【发布时间】:2016-07-07 08:50:26
【问题描述】:

我目前正在为 node.js 网站开发授权功能。我正在使用 Sequelize 作为 ORM 和 Passport 作为登录管理器。为了启用授权功能,我想向请求对象(即["manageDelete", "manageAdd", "userManage"])添加一个授权名称数组(只是字符串)。我想在passport.deserializeUser() 方法中做到这一点。

以下是一些附加信息:

授权存储在名为authorizations 的 MySQL 数据库表中。该表在n to m 关系中与另一个名为roles 的表相关联(我最终想要实现的是将授权捆绑在一起,以便更轻松地管理授权)。

我对异步代码有很大的问题,因为这个话题对我来说很新。我的代码来积累用户角色的所有授权是这样的:

passport.deserializeUser(function (id, done) {
  var currUser;
  models.User.findById(id)
  .then((user) => {
    currUser = user;
    //gets array of associated roles for this user
    return user.getRoles(); 
  })
  .then((roles) => {
    var authArr = [];
    roles.forEach((role) => {
      //gets array of associated authorizations for this role
      role.getAuthorizations().then((auths) => {
        auths.forEach((auth) => {
          authArr.push(auth.name);
        });
      });
    });
    return authArr;        
  })
  .done((authArr) => {
    done(null, {user: currUser, authArr: authArr});
  });
});

我知道由于异步性,done() 方法在任何承诺解决之前被调用,但我找不到任何方法来防止这种情况发生。我尝试了无数不同的模式(例如:https://www.joezimjs.com/javascript/patterns-asynchronous-programming-promises/ 或 async.js'),但我无法让它工作。

我做错了什么?有没有不使用任何附加模块的解决方案?帮助将不胜感激。提前致谢!

【问题讨论】:

    标签: javascript node.js asynchronous sequelize.js


    【解决方案1】:

    Serialize 使用 bluebird 承诺,而 bluebird 有一个 .each 方法可以满足您的需求。我认为这比以前的解决方案要简洁得多。附带说明一下,您使用箭头函数的事实表明您正在使用 es6,在这种情况下,我更喜欢 const/let 而不是 var。以下应该可行,但您也许可以使用 bluebird 的 map/reduce 方法提出一个更优雅的解决方案:

    passport.deserializeUser(function (id, done) {
      let currUser;
      const authArr = [];
      return models.User.findById(id)
        .then((user) => {
          currUser = user;
          //gets array of associated roles for this user
          return User.getRoles();
        })
        .each((role) => {
        //gets array of associated authorizations for this role
          return role.getAuthorizations().each((auth) => {
             authArr.push(auth.name);
          });
        })
        .then(() => {
          done(null, {user: currUser, authArr: authArr});
        });
     });
    

    【讨论】:

      【解决方案2】:

      您的代码中的问题是您没有在第二个then() 中返回承诺,因此空的authArr 会立即返回。

      你应该做的是:

      1. 在您的第二个then() 中返回一个承诺;
      2. 使用async 之类的方法确保在解决承诺之前完成所有role.getAuthorization() 调用。

      我会这样做。

      passport.deserializeUser(function (id, done) {
        var currUser;
        models.User.findById(id)
        .then((user) => {
          currUser = user;
          //gets array of associated roles for this user
          return user.getRoles(); 
        })
        .then((roles) => {
          return new Promise((resolve, reject) => { // Return a promise here. The next then() will wait for it to resolve before executing.
            var authArr = [];
            async.each(roles, (role, callback) => { // Use async.each to have the ability to call a callback when all iterations have been executed
              //gets array of associated authorizations for this role
              role.getAuthorizations().then((auths) => {
                auths.forEach((auth) => {
                  authArr.push(auth.name);
                });
                callback(); // Tell async this iteration is complete.
              });
            }, (err) => { // Only called when all iterations have called callback()
              if(err) reject(err);
              resolve(authArr); // Resolve the promise so the next .then() is executed
            });
          });
        })
        .then((authArr) => {
          done(null, {user: currUser, authArr: authArr});
        });
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-06-17
        • 2016-08-18
        • 1970-01-01
        • 2012-10-12
        • 1970-01-01
        相关资源
        最近更新 更多