如何限制/控制过多的异步调用?

时间:2016-02-15 02:37:35

标签: javascript node.js asynchronous promise

我是Node的新手,但我知道编写同步函数是件坏事。我锁定了事件循环或其他东西......所以把所有东西写成异步都很好。

但在某些情况下,编写所有异步可能都很糟糕。例如,我有一个函数进行API调用(到第三方API服务),然后我必须将结果写入数据库。我需要在很短的时间内完成这一工作,例如500次。

调用此API 500次异步,然后写入数据库500次async可能会禁止我从API服务(限制)和重载我的数据库服务器。

控制或限制此类内容的最佳方法是什么?我想让事情保持同步,所以它很有效,但我根本无法采取上述方法。

我研究了一些Promise throttling方法。这是解决此类问题的正确方法吗?有更好的方法吗?

3 个答案:

答案 0 :(得分:1)

我不确定Promise throttle是如何工作的,我相信Promise是一种比setTimeout更好的方法,并承诺它更基于事件,我的问题是npm包是的,一旦你的电话完成,它就不提供回叫选项,我的实现将是:



class PromiseThrottler {
  constructor(maxParallelCalls) {
      this.maxParallelCalls = maxParallelCalls;
      this.currentCalls = 0; // flag holding the no. of parallel calls at any point
      this.queue = []; // queue maintaining the waiting calls
    }
    // pormiseFn - the fn that wraps some promise call the we need to make, thenChain -  callback once your async call is done, args- arguments that needs to be passed to the function
  add(promiseFn, thenChain, ...args) {
    this.queue.push({
      promiseFn, thenChain, args
    });
    this.call();
  }
  call() {
    if (!this.queue.length || this.currentCalls >= this.maxParallelCalls) return;
    this.currentCalls++;
    let obj = this.queue.shift();
    let chain = obj.args.length ? obj.promiseFn(...obj.args) : obj.promiseFn();
    if (obj.thenChain) chain.then(obj.thenChain);
    chain
      .catch(() => {})
      .then(() => {
        this.currentCalls--;
        this.call();
      });
    this.call();
  }
}

//usage 

let PT = new PromiseThrottler(50)
  , rn = max => Math.floor(Math.random() * max) // generate Random number
  , randomPromise = id => new Promise(resolve => setTimeout(() => resolve(id), rn(5000))) // random promise generating function
  , i = 1
  , thenCall = id => {
    console.log('resolved for id:', id);
    let div = document.createElement('div');
    div.textContent = `resolved for id: ${id}`;
    document.body.appendChild(div);
  };

while (++i < 501) PT.add(randomPromise, thenCall, i);
&#13;
&#13;
&#13;

答案 1 :(得分:1)

async npm包很棒,有几种解决方案可以在这种特殊情况下使用。一种方法是使用具有设置并发限制的queue(直接从async README获取的示例):

// create a queue object with concurrency 2
var q = async.queue(function (task, callback) {
    console.log('hello ' + task.name);
    callback();
}, 2);

// assign a callback
q.drain = function() {
    console.log('all items have been processed');
}

// add some items to the queue
q.push({name: 'foo'}, function (err) {
    console.log('finished processing foo');
});

github.com/caolan/async#queue

在您的特定情况下,只需等待callback(),直到您等待的时间或交易细节完成为止。

答案 2 :(得分:-1)

限制此操作的一种简单方法是使用setTimeout并执行这样的“递归”循环。

function asyncLoop() {
  makeAPICall(function(result) {
    writeToDataBase(result, function() {
      setTimeout(asyncLoop, 1000);
    });
  });
}

当然,你也可以在承诺中使用相同的策略。