Как оптимизировать этот блок кода с момента его асинхронного - PullRequest
0 голосов
/ 11 ноября 2018
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 голосов
/ 12 ноября 2018

Учитывая, что вы не указываете, в каком контексте это находится, я сделаю несколько предположений:

  1. Я предполагаю, что _data.read() уже не поддерживает возврат обещания.
  2. Я предполагаю, что этот код должен либо вызывать функцию обратного вызова, либо возвращать обещание.

Мой (несколько наивный) подход к этому был бы:

  1. Карта orderItems в Обещания для каждой цены этого предмета.
  2. Отобразить результат в общую сумму
  3. Вернуть полученное обещание ИЛИ вызвать обратный вызов.

Вот аннотированный пример того, как вы можете это сделать:

// 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.
    }
)

Обратите внимание, что с помощью обещаний, функций стрелок и понимания массива (map и reduce) мы избегаем сложной и сложной для отслеживания мутации переменных и циклов. Это «функциональный» стиль программирования, и, хотя его труднее освоить, в целом он безопаснее и чище, чем альтернативы. Я предлагаю не торопиться, чтобы понять, как это работает, поскольку это поможет вам написать код, который с меньшей вероятностью будет иметь ошибки, когда вы имеете дело со сложными вещами, такими как асинхронность.

Наконец, я не запускал этот код. Это может иметь ошибку или два. Не стесняйтесь просить разъяснений или если это не работает.

Удачи!

P.S. Я не стал использовать async / await, потому что я думаю, что это будет менее понятно, чем прямое использование Обещаний, а использование Promise.all необходимо для параллелизма в любом случае. Здесь абсолютно возможно использовать их с хорошим эффектом, но я оставлю это как упражнение для ОП.

0 голосов
/ 12 ноября 2018

Вот как вы можете читать элементы в последовательности, используя обещания ( async / await flavor):

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. Однако, если это не так, он может «обещать», используя функцию promisify в nodejs ' util module

...