Я попытался написать функцию, которая одновременно запрашивает только ограниченное количество фиктивных конечных точек. Как только один запрос будет разрешен, будет получен следующий. Вот мой код:
const requestQueue = (endpoints, callback, limit = 3) => {
while (endpoints.length > 0) {
if (limit > 0) {
const slice = endpoints.splice(0, limit)
for (const endpoint of slice) {
limit--
fetchMock(endpoint)
.then(data => callback(data))
.catch(err => callback(err))
.finally(() => limit++)
}
}
}
}
function fetchMock(endpoint) {
return Promise.resolve(endpoint)
}
requestQueue([1, 2, 3, 4, 5], data => console.info(data))
Он входит в бесконечный цикл и падает. Что я упускаю или неправильно понимаю? Помоги пожалуйста.
🤔 А знаете ли вы, что...
JavaScript позволяет создавать мобильные приложения для iOS и Android с использованием фреймворков, таких как React Native и NativeScript.
Используйте Promise.all
для одновременного запуска элементов среза в пакете:
const requestQueue = async (endpoints, callback, limit = 3, offset = 0) => {
const slice = endpoints.slice(offset, offset + limit);
if (!slice.length){
return;
}
// map slice into fetchMock calls and await the batch
await Promise.all(slice.map(endpoint => fetchMock(endpoint).then(callback).catch(callback)));
// delay between slices, remove if not needed
await new Promise(resolve => setTimeout(resolve, 1000));
// run the next slice
return requestQueue(endpoints, callback, limit, offset += limit);
}
function fetchMock(endpoint) {
return new Promise(resolve => setTimeout(() => resolve(endpoint), Math.random()*1000));
}
requestQueue([1, 2, 3, 4, 5], data => console.info(data))
Причиной бесконечного цикла является уменьшение переменной limit
до нуля внутри цикла for of
; в результате условие while
цикла endpoints.length > 0
никогда не оценивается как false
.
endpoints.length
всегда больше нуля, потому что вы не позволяете циклу войти в блок if
после первой итерации. После первой итерации limit
равно нулю, поэтому limit > 0
не равно true
после первой итерации. В результате цикл застревает в бесконечном цикле.
Теперь вы можете сказать: «Но я увеличиваю limit
внутри обратного вызова finally
, поэтому limit
не всегда равно нулю».
Для увеличения limit
функция обратного вызова finally
должна быть выполнена, НО этого никогда не происходит, потому что асинхронный код (обратные вызовы then
, catch
и finally
вызываются асинхронно) выполняется после завершения выполнения синхронного кода, НО в вашем случае выполнение синхронного кода никогда не заканчивается, потому что он застрял в цикле while
. Вы не дали finally
обратному вызову шанса быть казненным.
На самом деле ни один из обратных вызовов промисов не вызывается в вашем коде, потому что синхронное выполнение кода должно завершиться для их вызова, и вы надеетесь завершить синхронное выполнение кода после вызова асинхронных обратных вызовов. Ваш код застрял в тупике.
Поскольку вы хотите инициировать запрос к другой конечной точке, как только любой из первоначально запущенных запросов будет завершен, следующий код показывает это в действии:
let offset = 0;
function makeRequest(endpoints, callback) {
console.info('fetching endpoint ' + endpoints[offset]);
fetchMock(endpoints[offset++])
.then(callback)
.catch(callback)
.finally(() => {
if (offset < endpoints.length) {
requestQueue(endpoints, callback);
}
});
}
const requestQueue = (endpoints, callback, limit = 3) => {
if (offset >= endpoints.length) {
console.info("returning");
return;
}
if (offset === 0) {
for (let i = 0; i < limit; i++) {
makeRequest(endpoints, callback);
}
} else {
makeRequest(endpoints, callback);
}
};
function fetchMock(endpoint) {
// random delay between 1 to 4 seconds
const randomDelay = (Math.random() * (5 - 1) + 1) * 1000;
return new Promise(res => setTimeout(res, randomDelay, endpoint));
}
requestQueue([1, 2, 3, 4, 5], data => console.info(data));
.as-console-wrapper { max-height: 100% !important; top: 0; }