Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 05.12.2020, 17:08
Аспирант
Отправить личное сообщение для dc65k Посмотреть профиль Найти все сообщения от dc65k
 
Регистрация: 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.
Ответить с цитированием
  #2 (permalink)  
Старый 05.12.2020, 17:18
Профессор
Отправить личное сообщение для Nexus Посмотреть профиль Найти все сообщения от Nexus
 
Регистрация: 04.12.2012
Сообщений: 3,726

Имхо, у вас реализация удовлетворяет только первому условию, остальные мимо.

Сообщение от dc65k
2. Одновременно должно выполняться не более указанного числа запросов.
Из-за await у вас единовременно будет выполняться всегда только 1 запрос, не более. А из-за 14-й строки выполниться вообще не более «maxNumberRequests» запросов, остальные будут проигнорированы.

Сообщение от dc65k
Должен возвращаться массив результатов в той же последовательности, что и адреса запросов.
У вас вообще ничего не возвращается. Даже если возвращать переменную response, то вернется только результат последнего запроса.

Сообщение от dc65k
4. Нельзя делать повторные запросы на дублирующиеся адреса (при этом результат всё равно должен присутствовать в результирующем массиве).
Вы удаляете все дубликаты из массива «urls», это ок, но не учитываете, что результат для этих дубликатов "всё равно должен присутствовать в результирующем массиве".

Последний раз редактировалось Nexus, 05.12.2020 в 17:21.
Ответить с цитированием
  #3 (permalink)  
Старый 05.12.2020, 23:30
Аватар для voraa
Профессор
Отправить личное сообщение для voraa Посмотреть профиль Найти все сообщения от voraa
 
Регистрация: 03.02.2020
Сообщений: 2,704

Я попытался написать.
У меня работало.
Использовал 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.
Ответить с цитированием
  #4 (permalink)  
Старый 06.12.2020, 02:57
Аватар для Alexandroppolus
Профессор
Отправить личное сообщение для Alexandroppolus Посмотреть профиль Найти все сообщения от Alexandroppolus
 
Регистрация: 25.10.2016
Сообщений: 1,005

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

// запускатель асинхронной функции для параметров
/*
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.
Ответить с цитированием
  #5 (permalink)  
Старый 06.12.2020, 12:14
Аспирант
Отправить личное сообщение для dc65k Посмотреть профиль Найти все сообщения от dc65k
 
Регистрация: 19.05.2020
Сообщений: 46

Всем спасибо за помощь.
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Как реализовать отправку содержимого из iframe нажатием комбинации клавиш ctrl+enter Naizer Общие вопросы Javascript 4 27.11.2013 17:39
Подскажите как реализовать такую задачу Gasherez (X)HTML/CSS 0 20.09.2013 19:59
Управление скроллом "а-ля тач" HonesT Элементы интерфейса 2 27.08.2013 14:25
Как это реализовать на JS Bezlyj Общие вопросы Javascript 6 06.03.2012 09:20
Как реализовать? Fliand Элементы интерфейса 4 22.08.2009 19:47