【问题标题】:Node.js - how to call a Facebook API within a for loop without exceeding rate limit?Node.js - 如何在不超过速率限制的情况下在 for 循环中调用 Facebook API?
【发布时间】:2018-03-05 02:58:02
【问题描述】:

我有一个包含近 200 万个 Facebook idSender 的数组,我想对其进行迭代,为每个人调用一个 Facebook API。 现在,由于异步性,如果我启动一个简单的 for 循环,Facebook 会在 4-5 分钟后返回给我一个速率限制超出错误。 经过一些尝试,我发现避免速率限制的理想延迟是 20 毫秒。

所以我尝试了这个解决方案:我将函数包装在 Promise 中,并使用 setTimeout 设置延迟。

async function asyncLoop(values) {
  var prm = await new Promise(function(resolve, reject) {
    setTimeout(function() {
        for (var i=0; i<values.length; i++) {
            check(values[i].idSender, values[i].iscrizione, values[i].id_sito)
            if(checked == values.length) {
                resolve()
            }
        }
    },20);
  });
  return prm;
}

asyncLoop(idSenderArray)

但是这个解决方案不起作用,而且我也知道使用 setTimeout 函数来操纵 Javascript 中的异步性几乎从来都不是一个好主意,事实上我什至不能说它到底发生了什么。

对我的功能可能进行的修改有什么建议吗?关于可以工作的全新实现的任何想法? 谢谢

【问题讨论】:

  • 在这里尝试 async.eachLimit 而不是 promises?通过限制并发调用而不是每次调用花费的时间,您可以实现相同的目标。 caolan.github.io/async/docs.html#eachLimit
  • 每 2M 项延迟 20 毫秒为您提供 11 小时 7 分钟的运行时间。你多久做一次......?

标签: javascript node.js asynchronous promise


【解决方案1】:

在 for 循环中只需 await 一次:

const timer = ms => new Promise(res => setTimeout(res, ms));

async function asyncLoop(values) {
  for (var i = 0; i < values.length; i++) {
    await timer(20);
    check(values[i].idSender, values[i].iscrizione, values[i].id_sito)
  }
}

【讨论】:

  • 我假设check 是一个异步函数,因此我也建议将此函数基于promise,然后也使用await check,否则请求可能会由于网络延迟等而开始交织,然后回到速率限制错误。
【解决方案2】:

你也可以用 Promise 做类似的事情;

var apiCall = (url,n) => new Promise((v,x) => setTimeout(v,50,`the result of API call @ ${url} no ${n}`)),
    calls   = Array(20).fill()
                       .map((_,i) => new Promise((v,x) => setTimeout(v,20*i))
                                         .then(_ => apiCall("http://facebook.com",i))
                                         .then(v => console.log(`I am doing something with ${v}`)));
.as-console-wrapper{
height: 100%;
max-height: 100% !important
}

【讨论】:

    【解决方案3】:

    或者,也可以将 setTimeout 与 Promise 一起使用:

    async function asyncLoop(values) {
        let apiCallPromises = values.map((value, index)=> new Promise(resolve=>{
            setTimeout(_=>{
                check(value.idSender, value.iscrizione, value.id_sito)
                resolve()
            }, index*20)
        }))
    
        return Promise.all(apiCallPromises);
    }
    

    相当严格,它将每个值映射到 check() 调用,每个后续请求的延迟为 20n ms。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-08
      • 1970-01-01
      • 1970-01-01
      • 2018-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多