我过去6个月一直在使用NodeJS进行编码,但我仍然没有关于异步和承诺概念的清晰图片。现在回答这个问题,我将使用来自MongoDB的Mongoose获取记录,这可能让branchIds在每次迭代中执行一个简单的for循环,执行异步的MongoDB操作(因为MongoDB / Mongoose操作是promises)。如您所知,for循环是同步的,但我的函数在for循环结束前返回值。怎么会发生?如果我的问题不明确,请附上代码,请将其留作评论。
const restManageChef = (params, query, body) => {
if (query && parseBoolean(query.superChef)) {
body = Object.assign(body, { role: 'SUPER-CHEF' });
} else {
body = Object.assign(body, { role: 'RES-CHEF' });
}
return restPUT(params, query, body).then(chef => {
return userModel
.findOne({ restaurantCode: chef.restaurantCode, type: 'RES-ADMIN' })
.then(resAdminDetails => {
log.debug({ Chef: chef }, 'Chef Details');
if (chef.role === 'SUPER-CHEF') {
log.debug({ BranchIds: resAdminDetails.branchIds }, 'BranchIds');
for (let i = 0; i < resAdminDetails.branchIds.length; i) {
log.debug({ BranchIds: resAdminDetails.branchIds[i] }, 'BranchIds');
pushChefId(resAdminDetails.branchIds[i], chef.pkid)
.then(restaurant => {
log.debug({ Restaurant: restaurant }, 'Restaurant Details');
})
.catch(err => {
log.error({ err });
throw err;
});
}
return chef;
} else if (chef.role === 'RES-CHEF') {
for (let i = 0; i < resAdminDetails.branchIds.length; i++) {
log.debug({ BranchIds: resAdminDetails.branchIds[i] }, 'BranchIds');
pushChefId(resAdminDetails.branchIds[i], chef.pkid)
.then(restaurant => {
log.debug({ Restaurant: restaurant }, 'Restaurant Details');
})
.catch(err => {
log.error({ err });
throw err;
});
}
return chef;
}
});
});
};
PushChefId功能
const pushChefId = (restaurantCode, chefId) => {
return userModel
.findOneAndUpdate({ restaurantCode }, { $addToSet: { chefIds: chefId } })
.exec()
.then(resAdmin => {
if (!resAdmin) return Promise.reject(`No RES-ADMIN found with restaurantCode - ${restaurantCode}`);
return storeModel.findByIdAndUpdate(restaurantCode, { $addToSet: { chefIds: chefId } }, { new: true });
});
};
答案 0 :(得分:1)
您正在以同步方式处理异步(在您的情况下为Promises)代码。
这是异步调用:
pushChefId(resAdminDetails.branchIds[i], chef.pkid)
.then(restaurant => {
log.debug({
Restaurant: restaurant
}, 'Restaurant Details');
})
.catch(err => {
log.error({
err
});
throw err;
});
基本上你逐个触发这个异步调用并立即跳转到return语句,而不必等待每个被激活的异步调用的完成。
我肯定会建议您查看的一种方法是async/await
,它基本上是编写异步代码的同步方式。
它可能会是这样的:
const decorateWithRole = (query, body) => {
return {
...body,
role: (query && parseBoolean(query.superChef) && "RES-CHEF") || "SUPER-CHEF"
};
};
const restManageChef = async(params, query, body) => {
const decoratedBody = decorateWithRole(query, body, parseBoolean);
const chef = await restPUT(params, query, body);
const resAdminDetails = await userModel.findOne({
restaurantCode: chef.restaurantCode,
type: "RES-ADMIN"
});
log.debug({
Chef: chef
}, "Chef Details");
if (["SUPER-CHEF", "RES-CHEF"].includes(chef.role)) {
log.debug({
BranchIds: resAdminDetails.branchIds
}, "BranchIds");
for (let i = 0; i < resAdminDetails.branchIds.length; i++) {
log.debug({
BranchIds: resAdminDetails.branchIds[i]
}, "BranchIds");
try {
const restaurant = await pushChefId(
resAdminDetails.branchIds[i],
chef.pkid
);
log.debug({
Restaurant: restaurant
}, "Restaurant Details");
} catch (err) {
log.error({
err
});
throw err;
}
}
return chef;
}
};
const pushChefId = async(restaurantCode, chefId) => {
const resAdmin = await userModel
.findOneAndUpdate({
restaurantCode
}, {
$addToSet: {
chefIds: chefId
}
})
.exec();
if (!resAdmin) {
return Promise.reject(
`No RES-ADMIN found with restaurantCode - ${restaurantCode}`
);
}
return storeModel.findByIdAndUpdate(
restaurantCode, {
$addToSet: {
chefIds: chefId
}
}, {
new: true
}
);
};
当然它可以通过并行承诺触发等进行优化。但是基本的解释应该足够了。
关键的变化在于:
for (let i = 0; i < resAdminDetails.branchIds.length; i++) {
log.debug({
BranchIds: resAdminDetails.branchIds[i]
}, "BranchIds");
try {
const restaurant = await pushChefId(
resAdminDetails.branchIds[i],
chef.pkid
);
log.debug({
Restaurant: restaurant
}, "Restaurant Details");
} catch (err) {
log.error({
err
});
throw err;
}
}
return chef;
}
};
await
函数上下文中的async
关键字将等待Promise
值的解析,并在没有Promise
包装的情况下返回它,只是原始的value或将接收抛出的错误,从而允许以基本try catch
的同步方式捕获它。
您可以在async等待here上阅读更多内容。
希望这有点澄清。