Асинхронный JavaScriptЗапуск асинхронных функцийhard

Асинхронная очередь с кэшированием

Эта задача является продолжением задачи promise-pool.

Допустим, теперь наша функция принимает не массив асинхронных функций, а массив URL, по которым нужно сделать запросы. В реальной жизни мы делали бы запрос с помощью стандартной функции fetch.

В той задаче функция для загрузки данных load передается в качестве аргумента. Она принимает строку и возвращает промис, разрешающийся строкой. Можно считать, что для всех url load что-то вернет.

const requestPromise = load("url1"); requestPromise.then(result => { console.log(result); // result — какая-то строка });

Необходимо реализовать функцию loadAll(urls, load, limit, cb), которая принимает массив строк urls, загружает их с помощью функции load и вызывает колбэк cb с массивом результатов запросов по этим urls. При этом есть два дополнительных требования:

  • нельзя, чтобы работало более limit функций одновременно;
  • если мы уже делали запрос по какому-то URL, то не делать его повторно;
  • колбэк cb вызывается ровно один раз, когда мы собрали все ответы функции load.

Функция loadAll не возвращает промис (она возвращается undefined). Вместо этого в момент, кода все данные собраны необходимо вызвать колбэк cb.

Пример

Пусть функция load работает разное время для разных URL: A — 2 секунды, B — 1 секунда, C — 1.4 секунды, D — 0.6 секунды, E — 1.2 секунды, F — 1.8 секунды, G — 0.8 секунды.

const urls = ["A", "B", "C", "A", "D", "B", "E", "B", "F", "G"]; loadAll(urls, 3).then((result) => { console.log(result); // массив с результатами });

При limit = 3 оптимальное время загрузки с учетом кэширования данных займет 3.4 секунды, т.к. мы не делаем запросы повторно.

A---------G--- B----D--F-------- C------E-----

Если бы не было кэширования, загрузка всех данных заняла бы 4.8 секунды, а диаграмма выглядела бы так:

A---------B----B---- B----A---------F-------- C------D--E-----G---

Это можно проверить, используя функцию run из задачи promise-pool:

function loadAll(urls, load, limit, cb) { const fns = urls.map(url => () => load(url)); run(fns, limit).then(cb); } const url2duration = { A: 2000, B: 1000, C: 1400, D: 600, E: 1200, F: 1800, G: 800, }; const load = (url) => new Promise(resolve => setTimeout(resolve, url2duration[url], url)); const urls = ["A", "B", "C", "A", "D", "B", "E", "B", "F", "G"]; console.time("run"); loadAll(urls, load, 3, (result) => { console.log(result); console.timeEnd("run"); });