JavaScript 承诺未按顺序触发
我一直在尝试有效地管理如何在 Express.js 应用中构建承诺。
目前,我有以下场景:在注册过程中,用户有一个可选的
组织名称
字段。如果填写了该字段,我需要创建
Organization
对象,然后将其
_id
添加到我要应用于
user
的其他信息中。如果没有
Organization name
,请继续并更新基本
user
信息。
<-- 目前,在创建
organization
之前正在更新
user
。 -->
//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');
})
您的代码中没有任何内容会等待第一个 Promise 解决后再继续执行后续工作。只要您调用
User.update
,工作就会立即开始,而当您在 Promise 执行器中使用该代码调用
new Promise
时,这项工作会同步完成。
相反,请等到上一个 Promise 解决后再执行此操作。我会通过将这些函数包装在可重复使用的支持承诺的包装器(
createOrganization
和
updateUser
)中来实现此目的:
// 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.promisify
或
promisify
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 级语法,因为您的代码似乎正在这样做……)
请注意,传入 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()
完成其工作之后。