2014-12-19 17 views
5

Ho una funzione request-promise che effettua una richiesta a un'API. Sono rate-limitata da questa API e continuo a ricevere il messaggio di errore:Coda promessa timer/acceleratore

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

sto correndo un paio di Promise.each loop in parallelo, che causa il problema, se corro solo un'istanza di Promise.each tutto funziona bene All'interno di queste chiamate Promise.each portano alla stessa funzione a con una chiamata request-promise. Voglio racchiudere questa funzione con un'altra funzione queue e impostare l'intervallo su 500 millisecondi in modo che uno request non venga creato uno dopo l'altro, o parallelo, ma impostato su tale ora, in coda. Il fatto è che ho ancora bisogno di queste promesse per ottenere i loro contenuti anche se ci vuole un tempo piuttosto lungo per ottenere una risposta.

C'è qualcosa che farà questo per me? Qualcosa in cui posso avvolgere una funzione e risponderà a intervalli regolari e non in parallelo o alle funzioni di fuoco una dopo l'altra?

Aggiornamento: Forse ha bisogno di essere promettere specifico, ho cercato di usare la funzione della valvola a farfalla di sottolineatura

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

E tutto quello che tornato era questo:

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

Puoi mostrarci parte del tuo codice reale in modo da avere qualcosa su cui lavorare? – jfriend00

+0

@ jfriend00 La mia implementazione è piuttosto specifica per un'API. Quello che ho è un wrapper per 'request' che usa un sacco di funzioni. Questo è il punto in cui posso frenare. Voglio avvolgere quel wrapper che contiene 'request' con una sorta di funzione di accodamento. – ThomasReggi

+0

Soluzione generica per questo tipo di problemi: [spex] (https://github.com/vitaly-t/spex). –

risposta

5

Aggiornamento

L'ultima risposta è sbagliata, funziona ma credo ancora di poter fare meglio:

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

Quindi è necessario effettuare le richieste funzione a livello della valvola a farfalla - va bene. Le promesse sono accodamento praticamente costruito.

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

Ora chiamate makeRequest saranno almeno 1000 ms a parte.

jfriend ha sottolineato che sono necessari due richieste al secondo e non una sola - questo è altrettanto facilmente risolvibili con una seconda coda:

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

Questo può essere generalizzata a n promesse utilizzando una matrice simile

+1

Un efficiente 2 chiamate/sec è un po 'più impegnato di questo. Ad esempio, dovresti essere in grado di inviare 2 chiamate immediatamente, ma poi devi aspettare un secondo per il terzo che arriva se viene subito, ma non aspettare affatto, se arriva un secondo dopo. – jfriend00

+0

Quindi dovrei essere bravo a '500'ms? – ThomasReggi

+0

@ThomasReggi dipende da quanto sia importante la velocità - Ho aggiunto una seconda soluzione che effettivamente fa "2 richieste al secondo" anche se la prima è più semplice. –