2014-12-19 6 views
5

У меня есть функция request-promise, которая обращается к API. Я ограничен по скорости этого API и я получаю сообщение об ошибке:Временная очередь ожидания/дроссельная заслонка

Exceeded 2 calls per second for api client. Reduce request rates to resume uninterrupted service. 

Я бегу пару Promise.each петель параллельно, который вызывает проблему, если я запускаю только один экземпляр Promise.each все работает нормально. Внутри этих вызовов Promise.each они приводят к той же функции a с вызовом request-promise. Я хочу обернуть эту функцию другой функцией queue и установить интервал 500 миллисекунд, чтобы request не был создан один за другим или параллелен, но установлен в это время в очереди. Дело в том, что мне все еще нужны эти обещания, чтобы получить их содержимое, даже если для получения ответа потребуется довольно много времени.

Есть ли что-нибудь, что сделает это для меня? Что-то, что я могу обернуть функцией, и она будет реагировать на заданный интервал, а не параллельно или запускать функции один за другим?

Update: Возможно, это действительно должно быть обещание конкретным, я пытался использовать функцию дроссельной Underscore в

var debug = require("debug")("throttle") 
var _ = require("underscore") 
var request = require("request-promise") 

function requestSite(){ 
    debug("request started") 
    function throttleRequest(){ 
    return request({ 
     "url": "https://www.google.com" 
    }).then(function(response){ 
     debug("request finished") 
    }) 
    } 
    return _.throttle(throttleRequest, 100) 
} 

requestSite() 
requestSite() 
requestSite() 

И все, что я вернулся был таков:

$ DEBUG=* node throttle.js 
throttle request started +0ms 
throttle request started +2ms 
throttle request started +0ms 
+0

Можете ли вы показать нам некоторые из вашего фактического кода, чтобы у нас было с чем работать? – jfriend00

+0

@ jfriend00 Моя реализация довольно специфична для API. У меня есть оболочка для 'request', которую использует целая куча функций. Вот в чем я могу задушить. Я хочу обернуть эту оболочку, содержащую 'request', с некоторой функцией очередей. – ThomasReggi

+0

Общее решение таких проблем: [spex] (https://github.com/vitaly-t/spex). –

ответ

5

Update

Последний ответ был неправильным, это работает, но я все еще думаю, что смогу сделать лучше:

// call fn at most count times per delay. 
const debounce = function (fn, delay, count) { 
    let working = 0, queue = []; 
    function work() { 
     if ((queue.length === 0) || (working === count)) return; 
     working++; 
     Promise.delay(delay).tap(() => working--).then(work); 
     let {context, args, resolve} = queue.shift(); 
     resolve(fn.apply(context, args)); 
    } 
    return function debounced() { 
     return new Promise(resolve => { 
      queue.push({context: this, args: arguments, resolve}); 
      if (working < count) work(); 
     }); 
    }; 
}; 

function mockRequest() { 
    console.log("making request"); 
    return Promise.delay(Math.random() * 100); 
} 

var bounced = debounce(mockRequest, 800, 5); 
for (var i = 0; i < 5; i++) bounced(); 
setTimeout(function(){ 
    for (var i = 0; i < 20; i++) bounced(); 
},2000); 

Так что вам нужно сделать запросы дроссельных функций шириной - это прекрасно. Обещания в очереди довольно много встроены.

var p = Promise.resolve(); // our queue 

function makeRequest(){ 
    p = p.then(function(){ // queue the promise, wait for the queue 
     return request("http://www.google.com"); 
    }); 
    var p2 = p; // get a local reference to the promise 
    // add 1000 ms delay to queue so the next caller has to wait 
    p = p.delay(1000); 
    return p2; 
}; 

Теперь звонки makeRequest будут не менее 1000 мс друг от друга.

jfriend указал, что вам нужно два запроса в секунду и ни один - это так же легко разрешимы второй очереди:

var p = Promise.resolve(1); // our first queue 
var p2 = Promise.resolve(2); // our second queue 

function makeRequest(){ 

    var turn = Promise.any([p, p2]).then(function(val){ 

     // add 1000 ms delay to queue so the next caller has to wait 
     // here we wait for the request too although that's not really needed, 
     // check both options out and decide which works better in your case 
     if(val === 1){ 
      p = p.return(turn).delay(1, 1000); 
     } else { 
      p2 = p2.return(turn).delay(1, 1000); 
     } 
     return request("http://www.google.com"); 
    }); 

    return turn; // return the actual promise 
}; 

Это может быть обобщена на n обещаниям, используя массив подобным

+1

Эффективное 2 вызова/сек немного более активное, чем это. Например, вы должны иметь возможность сразу отправить 2 вызова, но тогда вам нужно подождать секунду для третьего, который приходит, если он поступит сразу, но не ждать вообще, если он наступает через секунду. – jfriend00

+0

Так что я должен быть хорош в '500'ms? – ThomasReggi

+0

@ThomasReggi зависит от того, насколько важна скорость - я добавил второе решение, которое фактически выполняет «2 запроса в секунду», хотя первый из них проще. –