Может кто-нибудь сказать мне, почему моя очередь обещаний не работает?

Я пытаюсь написать простую функцию очереди обещаний, функция будет обрабатывать 50 задач с 10 одновременными обещаниями:

const sendTasks = () => {
  let tasks = 50;
  const concurrentCount = 10;
  let promisePool = 0;

  while (tasks > 0) {
    console.info(`current tasks: ${tasks}`);
    while (promisePool < concurrentCount && task > 0) {
      console.info("create promise");
      tasks--;
      promisePool++;
      const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve("haha");
        }, Math.floor(Math.random() * 3) * 1000);
      });

      promise.then((value) => {
        console.info(value);
        promisePool--;
      });

      console.info(`current promisePool: ${promisePool}`);
    }
  }  

  return "done";
};

Но когда я его выполняю, обещания, похоже, никогда не разрешаются, а затем застревают в цикле task>0 while. Может кто-нибудь объяснить мне, почему обещания никогда не разрешаются?

Вы никогда не уменьшаете concurrentCount. Это бесконечный цикл.

ddastrodd 14.02.2023 06:18

@ddastrodd Этот счетчик используется для ограничения моих одновременных номеров, поэтому он не будет уменьшаться.

Carl Tin 14.02.2023 06:23

@ddastrodd concurrentCount является константой и не предназначена для изменения — она контролирует, сколько промисов должно выполняться одновременно.

asportnoy 14.02.2023 06:24
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
3
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

JavaScript является однопоточным, за исключением случаев, когда это явно не так (веб-воркеры, многопроцессорность узлов, … — не промисы), поэтому ваш while (tasks > 0) { — это цикл занятости, который никогда не возвращает управление какому-либо циклу событий и дает таймерам возможность срабатывать.

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

const sendTasks = async () => {
  let tasks = 50;
  const concurrentCount = 10;
  const promisePool = new Set();

  while (tasks > 0) {
    console.info(`current tasks: ${tasks}`);
    while (promisePool.size < concurrentCount) {
      console.info("create promise");
      tasks--;
      const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve("haha");
        }, Math.floor(Math.random() * 3) * 1000);
      });

      promise.then((value) => {
        console.info(value);
        promisePool.delete(promise);
      });

      promisePool.add(promise);

      console.info(`current promisePool: ${[...promisePool]}`);
    }
    
    await Promise.race(promisePool);
  }

  // all tasks have been created here, but not necessarily completed
  await Promise.all(promisePool);

  return "done";
};

sendTasks().then(console.info);
Ответ принят как подходящий

Цикл while внутри синхронной функции никогда не передаст поток управления промисам .then или чему-либо еще. Вам нужно будет реструктурировать код, чтобы дождаться разрешения промисов без полного завершения функции sendTasks, а также без блокировки движка.

Один из подходов состоит в том, чтобы поместить каждое обещание в массив, а затем дождаться Promise.any в этом массиве. Удалите обещания из массива, когда они будут завершены, и рекурсивно поместите больше обещаний в массив. Затем вернитесь, когда в массиве больше не будет промисов.

const sendTasks = async () => {
  let tasks = 50;
  const concurrentCount = 10;
  let promisePool = 0;
  let promises = [];
  const enqueueNext = () => {
    if (!tasks) return;
    // creating this variable just for the sake of logging
    const thisTask = tasks--;
    console.info("create promise", thisTask);
    const prom = new Promise((resolve) => {
      setTimeout(() => {
        promises = promises.filter(p => p !== prom);
        console.info('resolving', thisTask);
        resolve("haha");
        // recursive asynchronous call; init the next promise, if there is one
        enqueueNext();
      }, Math.floor(Math.random() * 3) * 1000);
    });
    promises.push(prom);
  };
  for (let i = 0; i < concurrentCount; i++) {
    enqueueNext();
  }
  while (promises.length) {
    await Promise.any(promises);
  }
  console.info("done");
};
sendTasks();

Другие вопросы по теме