зациклите каждый элемент внутри обещания и отфильтруйте результат

#javascript

Вопрос:

Здравствуйте, у меня есть этот массив

 ids= [
       [234, 235, 236],
       [237, 238, 239, 240],
       [241, 242, 243, 244, 245]]
 

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

 priceMin= [
            [price: 100] // min price id 235, 
            [price: 120], // min price id 238
            [price: 180] // min price id 244
           ]
 

Я делаю это, но это не работает

 async getSkusAndProducts() {
      const id = this.ids;
      const app = { $axios: this.$axios };
      const promisesPrices = id.map((price) =>
        endPoint.getPrice(app, price)
      );
      const data = await Promise.all([
        promisesPrices.reduce((prev, curr) => (prev.id < curr.id ? prev : curr))
      ]);
      return data;
    }
 

Пожалуйста, помогите мне, спасибо

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

1. Не выполняйте reduce множество обещаний. Сделай это на data месте .

2. спасибо @bergi, но я не хочу повторять массивы внутри массива promise и находить результат массива, если товар имеет минимальную цену

3. Что бы вы ни хотели сделать с результирующим значением обещания, вы должны сделать это внутри .then() обратного вызова или после await .

Ответ №1:

Я полагаю, что вы получаете цены с вашего сервера, используя axios . Чтобы смоделировать эту часть, позвольте мне сначала реализовать для вас функцию макета endPoint.getPrice() следующим образом.

 /* dummy function to mock your axios call */
function getPrice (ids) {
  const prices = {
    "234": 300,
    "235": 100,
    "236": 400,
    "237": 140,
    "238": 120,
    "239": 150,
    "240": 310,
    "241": 210,
    "242": 500,
    "243": 400,
    "244": 180,
    "245": 192,
  };
  return new Promise((resolve, reject) => {
    resolve(ids.map(id => prices[id]));
  });
}
 

Это займет ваш ids массив и вернет прейскурант Promise . Ваше приложение axios также возвращается Promise . Так что это похоже. Вы можете увидеть, как это работает следующим образом:

 getPrice(ids[0]).then(console.log);
getPrice(ids[1]).then(console.log);
getPrice(ids[2]).then(console.log);
 

Теперь ваш код;

 const promisesPrices = id.map((price) =>
        endPoint.getPrice(app, price)
);
 

возвращает массив Promises , и программа продолжает выполнять следующую строку, не дожидаясь получения данных с сервера. Таким образом, вы не сможете на самом деле обрабатывать данные о ценах в следующих строках.

Самый простой способ в этом случае-передать обратный вызов асинхронной функции как:

 getPrice(id).then(prices => ({ price: Math.min(...prices) }))
 

Когда асинхронная задача выполнена (т. е. решена), прайс-лист передается обратному вызову. И массив цен теперь передается Math.min() оператору спреда.

Вот переписывание вашего getSkusAndProduct() . Я также использую Math.min() , чтобы получить самую низкую цену.

 async function getSkusAndProducts (ids) {
  const minPrices = await Promise.all(ids.map(id =>
    getPrice(id).then(prices => ({ price: Math.min(...prices) }))
  ));
  return minPrices;
}
 

На самом деле, приведенный выше код является избыточным. Написание ключевого await слова на самом деле не ждет. Он по-прежнему возвращает массив Promise и устанавливает для него значение minPrices . Это просто еще один способ написать стиль обратного вызова, например, .then(callback) в двух строках. В этом случае мы не хотим присваивать результат minPrices в обратном вызове, поэтому мы можем просто вернуть массив Promise напрямую следующим образом. Обратите внимание, что вам не нужен async модификатор, так как вы его не используете await . Это просто и красиво!

 function getSkusAndProducts (ids) {
  return Promise.all(ids.map(id =>
    getPrice(id).then(prices => ({ price: Math.min(...prices) }))
  ));
}
 

Вы можете запустить и посмотреть полный код ниже. Я использую избыточную версию getSkusAndProducts() ниже, так как она может выглядеть проще для чтения с minPrices переменными. Замените более короткой версией и убедитесь, что обе работают одинаково.

 const ids = [
  [234, 235, 236],
  [237, 238, 239, 240],
  [241, 242, 243, 244, 245]
];

/*
 * dummy function to mock your axios call
 * that returns Promise
 */
function getPrice (ids) {
  const prices = {
    "234": 300,
    "235": 100,
    "236": 400,
    "237": 140,
    "238": 120,
    "239": 150,
    "240": 310,
    "241": 210,
    "242": 500,
    "243": 400,
    "244": 180,
    "245": 192,
  };
  return new Promise((resolve, reject) => {
    resolve(ids.map(id => prices[id]));
  });
}

async function getSkusAndProducts (ids) {
  const minPrices = await Promise.all(ids.map(id =>
    getPrice(id).then(prices => ({ price: Math.min(...prices) }))
  ));
  return minPrices;
}

(async function main () {
  const minData = await getSkusAndProducts(ids);
  console.log(minData);
})();

/* or equivalently you can simply run as */
// getSkusAndProducts(ids).then(console.log); 

Приложение

Чтобы ответить на ваш дополнительный комментарий, вы можете запустить следующий код версии 2.

 function getSkusAndProductsV2 (ids) {
  return Promise.all([
    ...(ids.map(id => getPrice(id))),
    ...(ids.map(id => getPrice(id).then(prices => ({ price: Math.min(...prices) }))))
  ])
}
 

Первый getPrice() будет просто возвращать массив цен при разрешении. Второй с указанием then(callback) вернет минимальный прайс-лист следующим образом:

 [
  [ 300, 100, 400 ],
  [ 140, 120, 150, 310 ],
  [ 210, 500, 400, 180, 192 ],
  { price: 100 },
  { price: 120 },
  { price: 180 }
]
 

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

1. спасибо @waterloos, но у меня есть вопрос, если мое обещание. у всех есть два обещания внутри, как это const data = await Promise.all([promisesProducts, promisesPrices]); , и я хочу добавить Math min или reduce() для результата обещаний, а не для обещаний, как это сделать ? наконец, у вас есть один массив для продуктов и минимальных цен

2. Нет никакого вреда в том, чтобы вкладывать больше массивов обещаний Promise.all() . Просто убедитесь, что массив массивов выровнен как Promise.all([promissesProducts, promisesPrices.then(price => {do your work here})].flat()]) . Если вы не добавите обратный promissesProducts вызов , результат асинхронной задачи будет просто возвращен в конце. Вы можете поэкспериментировать с кодом в моем ответе, добавив еще getPrice() один вызов внутри Promise.all() , не устанавливая обратный вызов. Первый getPrice() вернет ваши данные о ценах, а второй с обратным вызовом вернет минимальные цены.

3. Я добавил приложение в ответ относительно вашего комментария. Я использовал оператор распространения вместо Array.flat() «хотя».

Ответ №2:

Это «вариация на тему», так как у меня нет доступа к вашему источнику AJAX. Я запускаю фрагмент с общедоступным typicode.com ресурс, и я получаю name атрибут (вместо цены) от каждого возвращенного запроса. Запросы группируются на двух уровнях и помещаются в Promise.all() вызовы. В конце массив D содержит все собранные данные ( id s и name s) для каждой группы, и я использую простое сокращение, чтобы найти «наименьший» элемент для каждой группы.

 const ids=[[234, 235, 236],
           [237, 238, 239, 240],
           [241, 242, 243, 244, 245]],
      res=[];
      url="https://jsonplaceholder.typicode.com/comments/";

Promise.all(ids.map(idgrp=>
 Promise.all(idgrp.map(id=>fetch(url id).then(r=>r.json()).then(d=>[d.id,d.name])))))
  .then(D=>{ // at this point all data is collected in D
   console.log(D); // all names
   console.log(D.map(d=>d.reduce((a,c)=>
    c[1]<a[1]?c:a))); // "smallest" name per group
}) 
 .as-console-wrapper {max-height:100% !important} 

Альтернативная версия, использующая ваши собственные данные (цены) и фиктивное обещание:

 const ids=[[234, 235, 236],
           [237, 238, 239, 240],
           [241, 242, 243, 244, 245]],
      fetchPrice=id=>new Promise(rslv=>rslv({"234":300,"235":100,"236":400,"237":140,
                                             "238":120,"239":150,"240":310,"241":210,
                                             "242":500,"243":400,"244":180,"245":192 }[id])),
      res=[];

Promise.all(ids.map(idgrp=>
// in the real code:       fetch(url id).then(r=>r.json()).then(d=>[id,d])))))
 Promise.all(idgrp.map(id=>fetchPrice(id)                 .then(d=>[id,d])))))
  .then(D=>{ // at this point all data is collected in D
   console.log(D); // all IDs and prices
   console.log(D=D.map(d=>d.reduce((a,c)=>
    c[1]<a[1]?c:a))); // "smallest" price per group
   console.log(D.map(d=>({price:d[1]}))) // if you want the prices only ...
}) 
 .as-console-wrapper {max-height:100% !important}