#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}