Асинхронность/ожидание всегда возвращает обещание или неопределенное

#javascript #node.js #asynchronous #async-await

Вопрос:

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

 const orderItems= orderData.map(async product=>{
          const productId=product.id.slice(0,product.id.length-5)

          const targetProduct=await dataSources.ProductAPI.product(productId)

          const name=targetProduct.name
          const quantity=218327

          await dataSources.OrderItemAPI.createOrderItem(orderId,productId,name,quantity,{ttlInSeconds:60*20})
        })
 

Затем я пытаюсь сопоставить каждый элемент заказа из приведенного выше массива orderItems с реальным продуктом в базе данных, например:

//мы находим продукт, соответствующий каждому элементу заказа

  const productsCorrespondingToOrderItems= orderItems.map(async orderItem=>{
      const trial=await orderItem
      console.log(trial) <--- returns undefined
      // OR just logging orderItem returns a promise
      await dataSources.TransactionAPI.findProductFromOrderItem(orderItem.productId).then(x=>console.log(x))
    })
 

Я проверил каждый товар из пунктов заказа, и все выглядит нормально. Проблема в том, что каждый orderItem из productsCorrespondingToOrderItems них возвращает восьмое обещание , когда я просто регистрирую его, или не определено, жду ли я его. Я действительно не понимаю, что я делаю не так? Большое вам спасибо !

Комментарии:

1. Вы ничего не возвращаете со своих map() обратных вызовов

2. @RobbyCornelissen Большое спасибо за быстрый ответ ! Я не знаю, как я это пропустил, попробую прямо сейчас !

3. То, что вы пытаетесь сделать, не имеет большого смысла. Array.map не работает с асинхронным кодом. Array.map предназначен только для синхронного кода. Если вы хотите выполнять последовательные циклы, используя async/await , вы должны использовать for for ... of циклы or. Если вы хотите выполнять параллельные асинхронные операции, вы должны создавать свои обещания, используя Array.map их, а затем использовать Promise.all для их ожидания.

4. @nicholaswmin — Для нас очень часто map при async обратном вызове создается массив обещаний, которые вы затем ждете (например, с помощью Promise.all или одним из других методов комбинатора обещаний).

5. @T. J. Краудер Да, действительно, но я уже упоминал об этом в последней части своего комментария.

Ответ №1:

async функции всегда возвращают обещание. Вы ничего не возвращаете из своих map обратных вызовов, поэтому, поскольку обратные вызовы являются async функциями, они возвращают обещания, которые выполняются undefined .

Если вы хотите дождаться выполнения всех обещаний, данных в ходе ваших map операций, используйте Promise.all результат. Promise.all возвращает обещание, которое будет выполнено с массивом значений выполнения данных вами обещаний (если все они выполнены) или отклонено при первом отклонении любого из них. Вы ждете этого обещания либо с await помощью (в async функции), либо с помощью вызова .then / .catch по нему.

Вот пример, в котором предполагается, что код, в котором вы выполняете, map не находится в async функции:

 // If not in an `async` function
Promise.all(orderData.map(async product => {
    const productId = product.id.slice(0, product.id.length - 5);
    const targetProduct = await dataSources.ProductAPI.product(productId);
    const name = targetProduct.name;
    const quantity = 218327;

    const orderItem await dataSources.OrderItemAPI.createOrderItem(
        orderId,
        productId,
        name,
        quantity,
        { ttlInSeconds: 60 * 20 }
    );
    return await dataSources.TransactionAPI.findProductFromOrderItem(orderItem.productId);
}))
.then(productsCorrespondingToOrderItems => {
    // ...use `productsCorrespondingToOrderItems` here...
})
.catch(error => {
    // ...handle/report error...
});
 

Обратите внимание, что это делает работу параллельно. Если вы хотите делать это последовательно, используйте async функцию и for-of цикл.

Ответ №2:

Вы ничего не вернули со своей карты, и карта не работает для асинхронного/ожидания. Сделайте это вместо того, чтобы вызывать api один раз за раз (последовательно). Если вы хотите ожидать несколько вызовов api одновременно (параллельно), используйте ответ от TJ Crowder.

 const orderItems= [];
for (const product of orderData) {
      const productId=product.id.slice(0,product.id.length-5)

      const targetProduct=await dataSources.ProductAPI.product(productId)

      const name=targetProduct.name
      const quantity=218327

      await dataSources.OrderItemAPI.createOrderItem(orderId,productId,name,quantity,{ttlInSeconds:60*20})
    })
    // push something to the orderItems array here!
}
 

Комментарии:

1. Большое спасибо!!