如何限制/控制太多的asynchronous调用?

我对Node很新,但是我明白编写syncronous函数是件坏事。 我locking了事件循环或者其他东西…所以把所有的东西都写成asyncronous是很好的。

但是在某些情况下,写一切asynchronous可能是不好的。 举例来说,我有一个API调用函数(对第三方API服务),然后我必须将结果写入数据库。 我需要在短时间内完成这个工作,例如500次。

调用这个API 500次asynchronous,然后写入数据库500次asynchronous可能会禁止我的API服务(节stream),并超载我的数据库服务器。

什么是控制或限制这样的事情的最好方法? 我想保持asynchronous,所以它是有效的,但我不能采取上述方法。

我研究了一些Promise节stream方法。 这是解决这类问题的正确方法吗? 有没有更好的方法来做到这一点?

async npm包是美好的,有几个解决scheme,可以在这种特殊情况下使用。 一种方法是使用具有集合并发限制的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()直到你正在等待的任何时间或事务细节已经完成。

我不确定Promise油门是如何工作的,我相信PromisesetTimeout相比是一个更好的方法,承诺它更基于事件,与npm包的问题是,一旦你的调用完成,它不提供callback选项,我的实现将是这样的:

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

限制这个的一个简单方法是使用setTimeout并像这样做一个“recursion”循环。

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

当然你也可以用承诺来使用同样的策略。