Ответы пользователя по тегу Асинхронное программирование
  • Как сделать задачу на параллелизм без использования promise?

    0xD34F
    @0xD34F Куратор тега JavaScript
    function parallel(tasks, onAllTasksComplete) {
      const results = [];
      let numCompleted = 0;
    
      function onTaskComplete(index, result) {
        results[index] = result;
        if (++numCompleted === tasks.length) {
          onAllTasksComplete(results);
        }
      }
    
      for (let i = 0; i < tasks.length; i++) {
        const onComplete = r => onTaskComplete(i, r);
        const result = tasks[i](onComplete);
        if (result !== undefined) {
          onComplete(result);
        }
      }
    }
    Ответ написан
    Комментировать
  • Как ограничить количество одновременных запросов на сервер?

    0xD34F
    @0xD34F Куратор тега JavaScript
    подскажите, что мне нужно подправить

    Всё нужно подправить. А ещё больше - сделать.

    Ваша функция никак не извещает вызывающий код о том, что запросы выполнены и с какими результатами; результаты запросов - вместо того, чтобы сохранять все, сохраняете только последний полученный; вместо того, чтобы ограничить количество одновременно выполняемых запросов вы пропускаете один (кажется, вы просто не понимаете, как работает forEach).

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

    Под хранение результатов запросов создадим массив.

    Так как url'ы могут повторяться, а запросы не должны, выполним группировку массива url'ов в объект: ключи - url'ы, значения - массивы индексов, под которыми данный url встречается в исходном массиве. Выдернем из этого объекта ключи - это будет массив уникальных url'ов.

    Также создадим два счётчика - количество отправленных запросов и количество полученных ответов.

    Собственно запрос. Первым делом проверяем, сколько ответов получено - если все, резолвим промис массивом результатов. В противном случае проверяем, сколько запросов отправлено - если не все, увеличиваем соответствующий счётчик, отправляем запрос; по получении ответа достаём массив индексов url'а, сохраняем результат, увеличиваем счётчик полученных ответов и пытаемся отправить следующий запрос.

    Ну и запускаем всё это дело в работу - обычный цикл for, в котором на каждой итерации выполняется запрос. Минимум одна итерация (это на тот случай, если массив url'ов пустой), максимум - количество уникальных url'ов или ограничение на количество одновременных запросов, меньшее из этих значений.

    Всё:

    function makeRequests(urls, max) {
      return new Promise(resolve => {
        const results = Array(urls.length).fill(null);
        const groupedUrls = urls.reduce((acc, n, i) => ((acc[n] ??= []).push(i), acc), {});
        const uniqueUrls = Object.keys(groupedUrls);
        let countRequests = 0;
        let countResponses = 0;
    
        for (let i = 0; i < Math.max(1, Math.min(max, uniqueUrls.length)); i++) {
          request();
        }
    
        function request() {
          if (countResponses === uniqueUrls.length) {
            resolve(results);
          } else if (countRequests < uniqueUrls.length) {
            const url = uniqueUrls[countRequests++];
            fetch(url)
              .then(result => result.json())
              .catch(error => error)
              .then(result => {
                groupedUrls[url].forEach(n => results[n] = result);
                countResponses++;
                request();
              });
          }
        }
      });
    }

    Ответ написан
    Комментировать
  • Как асинхронно перебрать массив, с задержкой между итерациями?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const delay = timeout => new Promise(r => setTimeout(r, timeout));

    async function asyncDelayedForEach(arr, callback, timeout) {
      for (let i = 0; i < arr.length; i++) {
        await callback.call(arr, arr[i], i, arr);
        await delay(timeout);
      }
    }
    
    // или
    
    const asyncDelayedForEach = (arr, callback, timeout) =>
      arr.reduce((acc, n, i, a) => acc
        .then(() => callback.call(a, n, i, a))
        .then(() => delay(timeout))
      , Promise.resolve());
    Ответ написан
    Комментировать
  • Как последовательно выполнять запросы внутри map?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Никак.

    Делайте async функцию, где будет цикл, в теле которого await fetch:

    async function xxx(arr) {
      const result = [];
    
      for (const item of arr) {
        result.push(await fetch(...));
      }
    
      return result;
    }
    Ответ написан
    Комментировать
  • Как последовательно вызвать асинхронные функции и записать результат в массив?

    0xD34F
    @0xD34F Куратор тега JavaScript
    async function chain(arr) {
      const result = [];
    
      for (const item of arr) {
        result.push(await item(result[result.length - 1]));
      }
    
      return result;
    }

    Или

    function chain(arr) {
      const result = [];
    
      return arr
        .reduce((prev, curr) => prev.then(curr).then(r => (result.push(r), r)), Promise.resolve())
        .then(() => result);
    }
    Ответ написан
  • Как при рекурсивном переборе древовидных объектов вызывать fetch на каждый item и на выходе получать измененный массив?

    0xD34F
    @0xD34F Куратор тега JavaScript
    async function addOptions(arr) {
      const result = [];
    
      for (const item of arr) {
        result.push({
          ...item,
          options: await fetchOptions(item.id),
          children: await addOptions(item.children),
        });
      }
    
      return result;
    }
    Ответ написан
    Комментировать
  • Как написать функцию Promise.race наоборот?

    0xD34F
    @0xD34F Куратор тега JavaScript
    const last = promises => new Promise((resolve, reject) => {
      let pending = promises.length;
    
      if (!pending) {
        resolve();
      } else {
        promises.forEach(n => n
          .then(result => --pending || resolve(result))
          .catch(error => --pending || reject(error))
        );
      }
    });
    Ответ написан
    2 комментария
  • Как написать асинхронную рекурсивную функцию?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Рекурсия не нужна:

    async function request(data) {
      let result = data;
    
      do {
        result = await запрос(result);
      } while (result не тот, который нужен);
    
      return result;
    }

    Но если очень хочется, тогда так:

    function requestRecursive(data) {
      return запрос(data).then(result => {
        return result тот, который нужен
          ? result
          : requestRecursive(result);
      });
    }
    Ответ написан
    1 комментарий
  • Это параллельное выполнение промиссов?

    0xD34F
    @0xD34F Куратор тега JavaScript
    В чём была проблема найти ответ на свой вопрос самостоятельно? Создаёте промисы, которые будут резолвиться через определённый промежуток времени, и смотрите, сколько пройдёт до получения результата в обоих случаях:

    const getNum = () => new Promise(r => setTimeout(r, 1000, Math.random() * 100 | 0));
    
    (async () => {
      console.time('xxx');
      const [ result1, result2 ] = [ await getNum(), await getNum() ];
      console.log(result1, result2);
      console.timeEnd('xxx');
    })();
    
    (async () => {
      console.time('yyy');
      const [ result1, result2 ] = await Promise.all([ getNum(), getNum() ]);
      console.log(result1, result2);
      console.timeEnd('yyy');
    })();
    Ответ написан
  • Почему асинхронная функция работает иначе?

    0xD34F
    @0xD34F Куратор тега JavaScript
    выполняются одновременно

    Всё-таки не одновременно. Поток выполнения один, так что... [разводит руками]

    Из-за чего у вас возникла иллюзия "одновременности" - вы результат работы синхронного кода (*) решили получать асинхронно. Вызвали первый раз showText - цикл открутился, замечательно, но демонстрации результата пока не происходит, потому что await. Вот когда поток выполнения освободиться, тогда да. А до того - надо второй вызов showText выполнить. Ну а когда вызовы showText отработают - вот тогда их результаты и показываются, сразу все.

    * Вы можете возразить - ...
    ...ну как же так, у меня там промис используется. А так. Недостаточно написать new Promise, чтобы получить желаемую асинхронность. Проведите небольшой эксперимент - откройте консоль, выполните

    console.log('щас будем создавать промис');
    new Promise(r => {
      console.log('создаём промис');
      setTimeout(() => {
        console.log('так, сейчас дёрнем резолв');
        r();
        console.log('резолв дёрнули');
      });
    }).then(() => console.log('зарезолвились, наконец-то'));
    console.log('промис создан');

    и посмотрите на порядок вывода сообщений. Потом то же самое ещё раз, но без оборачивания резолва в setTimeout. Подумайте. Подумав, можно будет сделать очевидный вывод: а промисы-то - синхронны (да-да, вот так), код внутри промисов выполняется сразу после вызова конструктора. Асинхронным является получение результата.
    Ответ написан
    5 комментариев
  • Почем время выполения двух промисов последовательно эквивалетно времени паралельного выполнения?

    0xD34F
    @0xD34F Куратор тега JavaScript
    результат времени как при параллельном выполнении

    А почему как? Они у вас выполняются параллельно. Чтобы было последовательно - второй должен создаваться в then первого.

    UPD. Вынесено из комментариев:

    Получается что new Promise( уже запускает на выполнение код? Его можно как то отложить?

    Можно сделать getA и getB функциями, возвращающими промисы - тогда будет как вы хотите:

    const getA = () => new Promise((resolve, reject) => {
        console.time('getA');
        setTimeout(() => {
            console.timeEnd('getA');
            resolve(4)
        }, 6000)
    });
    
    const getB = () => new Promise((resolve, reject) => {
        console.time('getB');
        setTimeout(() => {
            console.timeEnd('getB');
            resolve(2)
        }, 2000)
    });
    
    
    console.time('get sum');
    getA().then(a => {
        getB().then(b => {
            console.log(`result: ${a + b}`);
            console.timeEnd('get sum');
            })
        });


    UPD. Cabac_B недоумевает в комментариях по поводу "выполняются параллельно":

    Среда исполнения JS уже умеет параллельно?

    Ну не буквально же параллельно, и не буквально выполняются - смотрите код, о котором идёт речь. Имелось в виду, что оба промиса ждут своего разрешения одновременно.
    Ответ написан
    5 комментариев
  • Почему async/await в Array.prototype.map работает как-то не предсказуемо(для меня)?

    0xD34F
    @0xD34F Куратор тега JavaScript
    Такое ощущение будто в .map асинхронная функция не дожидается своих await, а сразу возвращает промис.

    Так точно. Функция, переданная в map, вызывается без всяких await'ов.

    Хотите массив со своими удвоенными значениями - используйте Promise.all:

    const arr = await Promise.all([1, 2, 3, 4, 5].map(async (value) => 2 * await asyncThing(value)));
    
    console.log(arr); // [2, 4, 6, 8, 10]
    Ответ написан
    1 комментарий