在Node.js中限制请求速率

7

我有一个数组。我可以使用foreach方法循环遍历它。

data.forEach(function (result, i) {

     url = data[i].url;

     request(url);

});

请求函数是向给定的URL发出HTTP请求。然而,同时进行所有这些请求会导致各种问题。

因此,我认为应该通过引入某种计时器来减慢速度。

但我不知道如何将forach循环与setTimeOut/setInterval结合起来。

请注意,我是在服务器上(nodejs)而不是在浏览器上执行此操作。

感谢您的帮助。


不完全符合您的要求,但您可以尝试使用队列:https://github.com/caolan/async#queue。仅在IE中同时执行10个请求。 - Jonathan Ong
各种问题”是什么意思? - Bergi
@saeed:你的 request() 函数是否需要一个回调函数?如果是这样,你不需要使用 setTimeout 来处理 5 个请求的限制。只需按顺序依次运行它们,在前一个完成后执行下一个即可。 - I Hate Lazy
@saeed:是的,我的下面的答案展示了如何实现。数组中下一个项目的执行将在前一个完成后才会发生。这并不是很复杂。如果你要使用NodeJS,你就必须习惯使用异步代码和回调函数。使用计时器是笨拙的。你怎么知道时间应该设置为多少呢?;) - I Hate Lazy
@user1689607,是的,我知道计时器很烦人。我只是用计时器向自己的服务器发出请求,但仍然遇到了限制5的问题。在第5个请求后,套接字会挂起。现在我将尝试您的代码,并以正确的方式完成它。 - saeed
显示剩余2条评论
8个回答

8

由于您的问题是全局性的,您应该调整您的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);
}

5
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
     );

 });

3

与其使用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);

1
许多上述解决方案,虽然对于少量请求是实用的,但在处理成千上万个请求时会导致页面阻塞和崩溃。每个定时器应该依次排队,而不是同时排队所有定时器。如果您的目标是拥有漂亮、简洁的代码和许多附加功能,则以下是适合您的解决方案。
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;
                }
            })();
        }
    };
}


1

您可以使用setTimeout延迟调用。以下代码将确保每个请求在其前一个请求后timerMultiPlier毫秒后被调用。

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

});

0
一个使用async/await进行快速限流的非常简单的解决方案是创建一个Promise,例如:
const wait = (delay) => new Promise((resolve, _) => {
  setTimeout(() => resolve(true), delay)
})

当您需要在 async 函数中暂停时,请等待它:

  //... loop process
  await wait(100)

注意:您需要使用 for 循环才能使其工作,forEach 不会等待。

0

关于这个问题的一些附加信息,仅供参考。

创建并使用异步版本而无需递归。

function onceEvery(msTime) {
  return {
    doForEach: function (eachFunc, paramArray) {
      var i = 0, Len = paramArray.length;
      (function rekurse() {
        if (i < Len) {
          eachFunc(paramArray[i], i, paramArray);
          setTimeout(rekurse, msTime);
          ++i;
        }
      })();
    },
    doForEachAsync: async function (eachFunc, paramArray, staticParamenters) {
      var i = 0, Len = paramArray.length;
      while (i < Len) {
        await (async function rekurse() {
          await eachFunc(paramArray[i], staticParamenters);
          setTimeout(() => { }, msTime);
          ++i;
        })();
      }
    }
  };
}

module.exports = {
  onceEvery
};

将此代码放入 .js 文件中,然后调用方式如下:
await throttle.onceEvery(100).doForEachAsync(loadJobFunction, arrayParam, { staticParam1, staticParam2, staticParam3 });

0

你可以按索引调整每个项的执行延迟,比如这样:

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

这将使每次迭代在前一次之后大约100毫秒执行。您可以更改100以更改延迟的数字。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接