JavaScript承诺不会按顺序触发

时间:2018-07-18 10:56:26

标签: javascript promise es6-promise

我一直试图有效地管理如何在Express.js应用程序中兑现承诺。

现在,我有以下情形:在注册过程中,用户具有一个可选的Organization Name字段。如果已填写,则需要创建Organization对象,然后将其_id添加到我要应用于user的其他信息中。如果没有Organization name,请继续并更新基本的user信息。

<-现在,在创建user之前,正在更新organization。 ->

//basic info passed from the signup form
info['first_name'] = req.body.first_name;
info['last_name'] = req.body.last_name;
info['flags.new_user'] = false;

//if organization name is passed, create object and store _id in user object. 
let create_organization = new Promise(function(resolve, reject) {
  if (req.body.organization_name !== "") { //check if name is sent from form
    Organization.create({
      name: req.body.organization_name
    }, function(err, result) {
      console.log(result);
      if (!err) {
        info['local.organization'] = result._id;
        resolve()
      } else {
        reject()
      }
    })
  } else {
    resolve()
  }
});

let update_user = new Promise(function(resolve, reject) {
  User.update({
    _id: req.user._id
  }, info, function(err, result) {
    if (!err) {
      console.log("Updated User!"); < --prints before Organization is created
      resolve();
    } else {
      reject();
    }
  })
});

create_organization
  .then(function() {
    return update_user;
  })
  .then(function() {
    res.redirect('/dash');
  })

2 个答案:

答案 0 :(得分:4)

您的代码中没有任何内容等待第一个承诺完成,然后再开始后续工作。只要您调用User.update,便会开始工作,当您在诺言执行器中使用该代码调用new Promise时,同步完成。

相反,请等到以前的承诺解决为止。我可以通过将这些函数包装在可重用的,启用了Promise的包装程序(createOrganizationupdateUser)中来做到这一点:

// Reusable promise-enabled wrappers
function createOrganization(name) {
    return new Promise(function(resolve, reject) {
        Organization.create({name: name}, function(err, result) {
            console.log(result);
            if (err) {
                reject(err);
            } else {
                resolve(result);
            }
        });
    });
}

function updateUser(id, info) {
    return new Promise(function(resolve, reject) {
        User.update({_id: id}, info, function(err, result) {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        })
    });
}

(您可以使用util.promisifypromisify npm模块来避免手动执行此操作。)

然后:

//basic info passed from the signup form
info['first_name'] = req.body.first_name;
info['last_name'] = req.body.last_name;
info['flags.new_user'] = false;

//if organization name is passed, create object and store _id in user object. 
(req.body.organization_name === "" ? Promise.resolve() : createOrganization(req.body.organization_name))
.then(function() {
    return updateUser(req.user._id, info);
})
.catch(function(error) {
    // handle/report error
});

(我坚持使用ES5级别的语法,因为您的代码似乎正在这样做...)

答案 1 :(得分:1)

参见invoked immediately,即传递到Promise构造函数中的所谓“执行函数”。这就是为什么您在两个远程过程调用之间实际上存在竞争条件的原因。为了解决这个问题,请让更新负责promise的创建:

function updateUser(userId, userInfo) {
  return new Promise(function(resolve, reject) {
    User.update({_id: userId}, userInfo, function(err, result) {
      if (err) { 
        reject(err);
      }
      else {
        resolve(result);
      }
    });
  });
}

...,然后在then()中调用此函数。这样,只有在updateUser被调用时才执行执行程序函数,而该执行器函数将在createOrganization()完成其工作之后被调用。