Я пытаюсь написать простую функцию очереди обещаний, функция будет обрабатывать 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. Может кто-нибудь объяснить мне, почему обещания никогда не разрешаются?
@ddastrodd Этот счетчик используется для ограничения моих одновременных номеров, поэтому он не будет уменьшаться.
@ddastrodd concurrentCount является константой и не предназначена для изменения — она контролирует, сколько промисов должно выполняться одновременно.
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();
Вы никогда не уменьшаете concurrentCount. Это бесконечный цикл.