05.12.2020, 17:08
|
Аспирант
|
|
Регистрация: 19.05.2020
Сообщений: 46
|
|
Как реализовать задачу с одновременными запросами на сервер?
Всем привет, есть задача со следующими условиями:
1. Написать функцию, получающую на вход массив ссылок и число, указывающее максимальное количество одновременных запросов.
2. Одновременно должно выполняться не более указанного числа запросов.
3. Должен возвращаться массив результатов в той же последовательности, что и адреса запросов.
4. Нельзя делать повторные запросы на дублирующиеся адреса (при этом результат всё равно должен присутствовать в результирующем массиве).
makeRequest([
'url1',
'url2',
'url1',
'url3'
], 2).then(([
result1,
result2,
result1,
result3
]) => {
});
Итак, моё решение:
const removingDuplicates = (arr) => {
return arr.filter((item, i, arr) => arr.indexOf(item) === i)
}
function makeRequests (urls, maxNumberRequests) {
const uniqueUrls = removingDuplicates(urls);
// console.log(uniqueUrls);
let response = null;
uniqueUrls.forEach(async (url, idx) => {
// console.log('url', url, idx);
if (idx === maxNumberRequests) {
return false;
}
response = await fetch(url, {
method: 'POST',
body: "statistics",
keepalive: true
});
});
return response;
}
makeRequests([
'url1',
'url2',
'url1',
'url3'
], 2);
Собственно, подскажите, что мне нужно подправить, полагаю, что я не совсем правильно понял условие задачи.
Последний раз редактировалось dc65k, 05.12.2020 в 17:22.
|
|
05.12.2020, 17:18
|
Профессор
|
|
Регистрация: 04.12.2012
Сообщений: 3,791
|
|
Имхо, у вас реализация удовлетворяет только первому условию, остальные мимо.
Сообщение от dc65k
|
2. Одновременно должно выполняться не более указанного числа запросов.
|
Из-за await у вас единовременно будет выполняться всегда только 1 запрос, не более. А из-за 14-й строки выполниться вообще не более «maxNumberRequests» запросов, остальные будут проигнорированы.
Сообщение от dc65k
|
Должен возвращаться массив результатов в той же последовательности, что и адреса запросов.
|
У вас вообще ничего не возвращается. Даже если возвращать переменную response, то вернется только результат последнего запроса.
Сообщение от dc65k
|
4. Нельзя делать повторные запросы на дублирующиеся адреса (при этом результат всё равно должен присутствовать в результирующем массиве).
|
Вы удаляете все дубликаты из массива «urls», это ок, но не учитываете, что результат для этих дубликатов "всё равно должен присутствовать в результирующем массиве".
Последний раз редактировалось Nexus, 05.12.2020 в 17:21.
|
|
05.12.2020, 23:30
|
|
Профессор
|
|
Регистрация: 03.02.2020
Сообщений: 2,744
|
|
Я попытался написать.
У меня работало.
Использовал GET вместо POST для удобства.
Может сложновато получилось, но как есть
const getpreq = async (areq, maxreq) => {
const mres = new Map(); // Набор запросов, выполняемых в данный момент url => индекс
const pres = []; // Массив, в который записываются обещания выполнения запросов
// В виде {responce: Promise (выполнения запроса для уникальных)
// Или {index: номер предыдущего запроса, для повторяющихся
// Функция ожидает, когда можно быдет выполнять следующий запрос
const mayaddreq = async () => {
if (mres.size < maxreq) return true;
// Формируем массив обещаний из выполняющихся запросов и ждем, когда какой-нибудь выполнится
await Promise.any([... mres.values()].map (ind => pres[ind]))
return true;
}
// Добавляет новый запрос
const addreq = (i, url) => {
let p = fetch(url, { // Обещание выполнение запроса
method: 'GET',
// body: "statistics",
// keepalive: true
})
let mp = p.then (res => { // Кошда запрос выполнится
mres.delete(url); // Удалить его из списка выполняемых
return {responce:res} // Вернуть ответ
})
mres.set (url, i) // Записать в список выполняемых
return mp // Возвращаем обещание
}
for (let i= 0; i< areq.length; i++) {
const url = areq[i];
let si = areq.indexOf(url)
if (si>=0 && si < i) { // Это повторяющийся запрос
pres.push( {index:si})
} else {
await mayaddreq () // Ждем, когда можно начать выполнять
pres.push (addreq (i, url)) // Выполняем запрос и записываем обещание в массив
}
}
return pres
}
// Функция получает массив вида [{responce: ответ responce от fetch} или {index: номер исходного responce для повторяющихся} ...]
// Возвращает массив результатов обработки responce
const getans = async (aresp) => {
let aansp = [] // массив обещаний обработки responce
aresp.forEach ((r, i, ar) => { // в данном случае я использую responce.text
aansp[i] = r.responce? r.responce.text() : Promise.resolve(aansp[r.index])
})
return Promise.all (aansp)
}
aurls = [
'testaj.php?num=1&val=10',
'testaj.php?num=2&val=20',
'testajm.php?num=3&val=30',
'testajm.php?num=4&val=40',
'testaj.php?num=5&val=50',
'testajm.php?num=6&val=60',
'testaj.php?num=7&val=70',
'testajm.php?num=6&val=60',
'testaj.php?num=2&val=20',
'testajm.php?num=6&val=60',
'testajm.php?num=3&val=30',
'testajm.php?num=8&val=80',
'testajm.php?num=8&val=80',
'testajm.php?num=8&val=80',
'testaj.php?num=9&val=90',
'testaj.php?num=1&val=10',
]
const main = async () => {
let prec = await getpreq (aurls, 5)
let aresp = await Promise.all (prec)
let ans = await getans (aresp)
console.log (ans)
}
main()
Зы Предполагается, что запросы выполняются без ошибок.
С обработкой ошибок сложнее будет.
Последний раз редактировалось voraa, 05.12.2020 в 23:39.
|
|
06.12.2020, 02:57
|
|
Профессор
|
|
Регистрация: 25.10.2016
Сообщений: 1,012
|
|
такие штуковины надо делать в параметризуемом виде.
вот, с обработкой ошибок
// запускатель асинхронной функции для параметров
/*
args: {
request: param => Promise
getKey: param => value
limit: number
params: array
}
return [...]
*/
function runRequests(args) {
if (!args.params) {
return Promise.resolve([]);
}
return new Promise(res => {
const results = new Array(args.params.length);
const map = new Map();
const req = []
for (let i = 0; i < args.params.length; ++i) {
const param = args.params[i];
const key = args.getKey ? args.getKey(param) : param;
let config = map.get(key);
if (!config) {
config = {
param: param,
indexes: [i]
};
map.set(key, config);
req.push(config);
} else {
config.indexes.push(i);
}
}
const limit = Math.min(args.limit || req.length, req.length);
let pos = 0;
let doneCount = 0;
function run() {
if (pos < req.length) {
const config = req[pos++];
args.request(config.param).then(
data => done(false, data, config),
error => done(true, error, config))
}
}
function done(error, value, config) {
// результат в комплексном виде, тут может быть как успешно, так и с ошибкой
const item = {
success: !error,
data: error ? undefined : value,
error: error ? value : undefined
};
for (let i = 0; i < config.indexes.length; ++i) {
results[config.indexes[i]] = item
}
doneCount++;
if (doneCount >= req.length) {
res(results);
} else {
run();
}
}
for (let i = 0; i < limit; ++i) {
run();
}
});
}
// пример асинхронной функции, которая получает параметр и возвращает промис
function asyncReverse(value) {
console.log('request ', value);
return new Promise((res, rej) => {
const r = Math.random();
const delay = Math.floor(2000 + 3000 * Math.random());
setTimeout(() => {
if (r < 0.00002) {
rej(r);
} else {
console.log('result for ', value);
res(value.split('').reverse().join(''));
}
}, delay);
});
}
// запуск для параметров
runRequests({
request: asyncReverse, // асинхронная функция
getKey: param => param, // ключ, по которому выявляем одинаковые параметры
limit: 3,
params: [
'1234',
'qazx',
'rwsxc',
'edcv',
'rfvb',
'edcv'
]
}).then(console.log)
смотреть в консоли результат можно
Последний раз редактировалось Alexandroppolus, 06.12.2020 в 02:59.
|
|
06.12.2020, 12:14
|
Аспирант
|
|
Регистрация: 19.05.2020
Сообщений: 46
|
|
Всем спасибо за помощь.
|
|
|
|