自异步以来如何优化此代码块
How to optimize this block of code since its async
var orderItems = userData.shoppingcart;
var totalPrice = 0;
userData.shoppingcart.forEach(function(itemName, i){
_data.read('menuitems', itemName, function(err, itemData){
if(!err && itemData)
{
totalPrice += itemData.price;
if(++i == userData.shoppingcart.length){
// Only here when all the itemNames have been read I should continue
}
}
});
});
如您所见,对 _data.read 的调用是异步的,因为我正在从文件中读取。
但我需要等待读取所有文件,这样我才能计算出总价格。这就是为什么我把那个条件 [ ++i == userData.shoppingcart.length ].
我是 javascript 和 nodejs 的新手,从来都不是一个很好的程序员,但无论如何我的观点是我确信这不是一个好方法,如果两个文件都被读取了怎么办同时,然后那个条件永远不会 运行 或者 totalPrice 的计算做得不好?
有人可以给我一些指导吗?
提前致谢!
这是使用 promises(async/await 风格)按顺序读取项目的方法:
var orderItems = userData.shoppingcart;
let totalPrice = 0;
for (let itemName of userData.shoppingcart) {
const itemData = await _data.read('menuitems', itemName);
totalPrice += itemData.price;
}
此示例假定 _data.read
支持 async/await
。但是,如果没有,它可以 "promisified" 使用 nodejs 的 util 模块
中的 promisify
函数
鉴于您没有指定这是在什么上下文中,我将做一些假设:
- 我认为
_data.read()
还不支持 return 承诺。
- 我假设此代码需要调用回调函数或 return 承诺。
我(有点幼稚)的做法是:
- 将
orderItems
映射到该商品每个价格的 Promises 中。
- 将结果映射到总计
- Return 结果承诺或调用回调。
这是一个带注释的示例,说明您可以如何操作:
// Promise.all takes an array of promises and returns
// a new promise that completes when all the promises in the array are complete.
const promiseOfPrices = Promise.all(
// Here we map all the items in the shopping cart into promises for each of their prices
userData.shoppingcart.map(
// The Promise object takes a single function that it will immediatly call with functions to resolve or
// reject the promise. I suggest reading up on Promises if you're not familiar with them.
itemName => new Promise((resolve, reject) => {
// Here, we have a `reject` and `resolve` function that will each complete the new promise,
// either in success or error respectfully.
// Do the actual read of your file or database or whatever
_data.read('menuitems', itemName, (err, itemData) => {
// If there was an error, reject this promise.
if (err) reject(err);
// Otherwise, we're successful and we resolve with the price of the item
else resolve(itemData.price);
});
})
)
);
// Now, we have a promise (promiseOfPrices) for all the prices of the items in the cart. We use `then` which will
// perform a transform on the result, much like the `map` function on an Array.
const promiseOfTotal = promiseOfPrices.then(
// Here we use the `Array.reduce` function to succinctly sum the values in the array.
arrayOfCartItemPrices => arrayOfCartItemPrices.reduce(
// For each item, reduce calls our function with the current sum and an item in the array. We produce a new
// sum by adding the sum to the item price.
(sum, itemPrice) => sum + itemPrice,
// This is the initial value for sum, 0.
0
)
);
如果你能return一个承诺,而你只想return全部,那么
return promiseOfTotal;
如果你有一个期望(错误,结果)的回调,那么做这样的事情:
promiseOfTotal.then(
result => callback(null, result),
error => callback(error, null),
)
如果你需要对结果做更多的工作,你可以用另一个 then:
promiseOfTotal.then(
priceSum => {
// Do work here
},
// Optionally handle errors here:
error => {
// Do error handling here.
}
)
请注意,通过使用承诺、箭头函数和数组理解(map
和 reduce
),我们避免了复杂且难以遵循的变量和循环突变。这是一种 "functional" 编程风格,虽然学习起来有点困难,但通常比其他选择更安全、更简洁。我建议花时间了解它是如何工作的,因为它会帮助您编写在处理异步等复杂事物时不太可能出现错误的代码。
最后,我没有运行这个代码。它很可能有一两个错误。如果它不起作用,请随时要求澄清。
祝你好运!
P.S。我没有开始使用 async
/await
,因为我认为它不如直接使用 Promises 清晰,而且并行性无论如何都需要使用 Promise.all
。在这里使用它们绝对有可能产生良好的效果,但我会将其作为练习留给 OP。
var orderItems = userData.shoppingcart;
var totalPrice = 0;
userData.shoppingcart.forEach(function(itemName, i){
_data.read('menuitems', itemName, function(err, itemData){
if(!err && itemData)
{
totalPrice += itemData.price;
if(++i == userData.shoppingcart.length){
// Only here when all the itemNames have been read I should continue
}
}
});
});
如您所见,对 _data.read 的调用是异步的,因为我正在从文件中读取。
但我需要等待读取所有文件,这样我才能计算出总价格。这就是为什么我把那个条件 [ ++i == userData.shoppingcart.length ].
我是 javascript 和 nodejs 的新手,从来都不是一个很好的程序员,但无论如何我的观点是我确信这不是一个好方法,如果两个文件都被读取了怎么办同时,然后那个条件永远不会 运行 或者 totalPrice 的计算做得不好?
有人可以给我一些指导吗? 提前致谢!
这是使用 promises(async/await 风格)按顺序读取项目的方法:
var orderItems = userData.shoppingcart;
let totalPrice = 0;
for (let itemName of userData.shoppingcart) {
const itemData = await _data.read('menuitems', itemName);
totalPrice += itemData.price;
}
此示例假定 _data.read
支持 async/await
。但是,如果没有,它可以 "promisified" 使用 nodejs 的 util 模块
promisify
函数
鉴于您没有指定这是在什么上下文中,我将做一些假设:
- 我认为
_data.read()
还不支持 return 承诺。 - 我假设此代码需要调用回调函数或 return 承诺。
我(有点幼稚)的做法是:
- 将
orderItems
映射到该商品每个价格的 Promises 中。 - 将结果映射到总计
- Return 结果承诺或调用回调。
这是一个带注释的示例,说明您可以如何操作:
// Promise.all takes an array of promises and returns
// a new promise that completes when all the promises in the array are complete.
const promiseOfPrices = Promise.all(
// Here we map all the items in the shopping cart into promises for each of their prices
userData.shoppingcart.map(
// The Promise object takes a single function that it will immediatly call with functions to resolve or
// reject the promise. I suggest reading up on Promises if you're not familiar with them.
itemName => new Promise((resolve, reject) => {
// Here, we have a `reject` and `resolve` function that will each complete the new promise,
// either in success or error respectfully.
// Do the actual read of your file or database or whatever
_data.read('menuitems', itemName, (err, itemData) => {
// If there was an error, reject this promise.
if (err) reject(err);
// Otherwise, we're successful and we resolve with the price of the item
else resolve(itemData.price);
});
})
)
);
// Now, we have a promise (promiseOfPrices) for all the prices of the items in the cart. We use `then` which will
// perform a transform on the result, much like the `map` function on an Array.
const promiseOfTotal = promiseOfPrices.then(
// Here we use the `Array.reduce` function to succinctly sum the values in the array.
arrayOfCartItemPrices => arrayOfCartItemPrices.reduce(
// For each item, reduce calls our function with the current sum and an item in the array. We produce a new
// sum by adding the sum to the item price.
(sum, itemPrice) => sum + itemPrice,
// This is the initial value for sum, 0.
0
)
);
如果你能return一个承诺,而你只想return全部,那么
return promiseOfTotal;
如果你有一个期望(错误,结果)的回调,那么做这样的事情:
promiseOfTotal.then(
result => callback(null, result),
error => callback(error, null),
)
如果你需要对结果做更多的工作,你可以用另一个 then:
promiseOfTotal.then(
priceSum => {
// Do work here
},
// Optionally handle errors here:
error => {
// Do error handling here.
}
)
请注意,通过使用承诺、箭头函数和数组理解(map
和 reduce
),我们避免了复杂且难以遵循的变量和循环突变。这是一种 "functional" 编程风格,虽然学习起来有点困难,但通常比其他选择更安全、更简洁。我建议花时间了解它是如何工作的,因为它会帮助您编写在处理异步等复杂事物时不太可能出现错误的代码。
最后,我没有运行这个代码。它很可能有一两个错误。如果它不起作用,请随时要求澄清。
祝你好运!
P.S。我没有开始使用 async
/await
,因为我认为它不如直接使用 Promises 清晰,而且并行性无论如何都需要使用 Promise.all
。在这里使用它们绝对有可能产生良好的效果,但我会将其作为练习留给 OP。