嵌套forEach和Asynchronity

时间:2016-07-06 14:10:42

标签: javascript node.js asynchronous sequelize.js

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

以下是一些其他信息:

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

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

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

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

我做错了什么?有没有使用任何额外模块的解决方案?帮助将非常感激。提前谢谢!

2 个答案:

答案 0 :(得分:0)

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

你应该做的是:

  1. 在您的第二个then();
  2. 中返回一个承诺
  3. 在解决承诺之前,请使用async之类的内容确保所有role.getAuthorization()来电完成。
  4. 我会这样做。

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

答案 1 :(得分:0)

Serialize使用bluebird promises,bluebird有一个.each方法可以满足你的需要。我认为这比以前的解决方案简洁得多。作为旁注,你使用箭头函数的事实表明你正在使用es6,在这种情况下我更喜欢const / let over 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});
    });
 });