дроссельные запросы в узле.JS


У меня есть массив. Я могу обойти его с помощью метода foreach.

data.forEach(function (result, i) {

     url = data[i].url;

     request(url);

});

Функция запроса делает http-запрос к данному url-адресу. Однако выполнение всех этих запросов одновременно приводит к разного рода проблемам.

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

Но я понятия не имею, как можно будет объединить цикл forach с setTimeOut/setInterval

Обратите внимание, что я делаю это на сервере (nodejs), а не на браузер.

Спасибо за помощь.

6 6

6 ответов:

Вместо setTimeout можно было бы запустить их последовательно. Я предполагаю, что у вашей функции request() есть параметр callback.

function makeRequest(arr, i) {
    if (i < arr.length) {
        request(arr[i].url, function() { 
                                i++; 
                                makeRequest(arr, i); 
                            });
    }
}

makeRequest(data, 0);

Если вам нужно немного больше времени между запросами, то добавьте setTimeout к обратному вызову.

function makeRequest(arr, i) {
    if (i < arr.length) {
        request(arr[i].url, function() { 
                                i++; 
                                setTimeout(makeRequest, 1000, arr, i); 
                            });
    }
}

makeRequest(data, 0);

Поскольку ваша проблема является глобальной, вы должны настроить свою функцию request так, чтобы одновременно выполнялось только 5 запросов - с использованием глобального статического счетчика. Если ваш запрос был раньше чем-то вроде

function request(url, callback) {
    ajax(url, callback);
}

Теперь используйте что-то вроде

var count = 0;
var waiting = [];
function request(url, callback) {
    if (count < 5) {
        count++;
        ajax(url, function() {
            count--;
            if (waiting.length)
                request.apply(null, waiting.shift());
            callback.apply(this, arguments);
        });
    } else
        waiting.push(arguments);
}
data.forEach(function (result, i) {

     url = data[i].url;

     setTimeout(
         function () {
              request(url);
         }, 
         1000 * (i + 1) // where they will each progressively wait 1 sec more each
     );

 });

Вы можете задержать вызов с помощью setTimeout. следующий код гарантирует, что каждый запрос будет вызван через timerMultiPlier миллисекунд от его предыдущего запроса.

var timerMultiPlier = 1000;
data.forEach(function (result, i) {
     setTimeout(function(){
           url = data[i].url;         
           request(url);
   }, timerMultiPlier*i );

});

Вы можете компенсировать задержку выполнения каждого элемента индексом, например:

data.forEach(function (result, i) { 
  setTimeout(function() {
    url = data[i].url; 
    request(url);
  }, i * 100);
}); 

При этом каждая итерация будет выполняться примерно через 100 миллисекунд после предыдущей. Вы можете изменить 100 на любое число, которое вам нравится, чтобы изменить задержку.

Многие из вышеперечисленных решений, будучи практичными для нескольких запросов, необоснованно заглушают и замуровывают страницу, когда речь идет о десятках тысяч запросов. Вместо того чтобы ставить в очередь все таймеры по одному, каждый таймер должен быть поставлен в очередь последовательно один за другим. Если ваша цель состоит в том, чтобы иметь хороший довольно пушистый код с большим количеством сахара и "goodie-goodies", то ниже приведено решение для вас.

function miliseconds(x) { return x }
function onceEvery( msTime ){
    return {
        doForEach: function(arr, eachF){
            var i = 0, Len = arr.length;
            (function rekurse(){
                if (i < Len) {
                    eachF( arr[i], i, arr );
                    setTimeout(rekurse, msTime);
                    ++i;
                }
            })();
        }
    };
}

Хорошая, красивая, пушистая сахарная глазурь использование:

onceEvery(
    miliseconds( 150 )
).doForEach(
    ["Lorem", "ipsum", "dolar", "un", "sit", "amet"],
    function(value, index, array){
        console.log( value, index );
    }
)

function miliseconds(x) { return x }
function onceEvery( msTime ){
    return {
        doForEach: function(arr, eachF){
            var i = 0, Len = arr.length;
            (function rekurse(){
                if (i < Len) {
                    eachF( arr[i], i, arr );
                    setTimeout(rekurse, msTime);
                    ++i;
                }
            })();
        }
    };
}