自异步以来如何优化此代码块

时间:2018-11-11 19:08:10

标签: javascript node.js asynchronous conditional

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的调用是异步的,因为我正在读取文件。

但是我需要等待读取所有文件,以便可以计算出totalPrice。这就是为什么我要设置条件[++ i == userData.shoppingcart.length]。

我一般来说对javascript和nodejs都是陌生的,从来都不是很好的程序设计师,但是无论如何,我的观点是我确信这不是一个好方法,如果同时读取两个文件然后该条件永远不会运行或totalPrice的计算做得不好?

有人可以给我一些指导吗? 预先谢谢你!

2 个答案:

答案 0 :(得分:2)

鉴于您没有指定其上下文,我将作一些假设:

  1. 我认为_data.read()已经不支持返回承诺。
  2. 我认为这段代码需要调用回调函数或返回promise。

我(有点天真)的做法是:

  1. 针对该商品的每个价格,将orderItems映射到Promises中。
  2. 将结果映射到总计
  3. 返回产生的promise或调用回调。

这是一个注释示例,说明您如何做:

// 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 promiseOfTotal;

如果您有一个预期的回调(错误,结果),请执行以下操作:

promiseOfTotal.then(
    result => callback(null, result),
    error => callback(error, null),
)

如果您需要对结果做更多​​的工作,则可以与另一个人一起做:

promiseOfTotal.then(
    priceSum => {
        // Do work here
    },
    // Optionally handle errors here:
    error => {
        // Do error handling here.
    }
)

请注意,通过使用Promise,箭头函数和数组推导(mapreduce),我们可以避免变量和循环的复杂且难以跟踪的变化。这是一种“功能性”的编程风格,虽然较难学习,但通常比其他方法更安全,更干净。我建议您花些时间了解它的工作原理,因为它可以帮助您编写在处理诸如异步之类的复杂事物时不太可能出现错误的代码。

最后,我没有运行此代码。它可能有一个或两个错误。随时要求澄清或不起作用。

祝你好运!

P.S。我不打算使用async / await,因为我认为它不如直接使用Promises清晰,而且无论如何,并行性都需要使用Promise.all。在这里绝对有可能很好地使用它们,但是我将其留给OP进行练习。

答案 1 :(得分:1)

这是您使用诺言(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。但是,如果没有,则可以使用nodejs的util模块中的promisify函数“承诺”