2017-10-09 32 views
5

Mam ogranicznik szybkości dla interfejsu API, którego używam, który pozwala na 20 żądań na sekundę. Wszystkie żądania są oparte na obietnicach, a obietnica zostanie rozwiązana przy użyciu danych API po otrzymaniu odpowiedzi.Promise.all zużywa całą moją pamięć RAM

Problem:

I Skonfiguruj promiseArray który zawiera 58K obiecuje wszystko czeka na odpowiedź. Tak powoli pamięć rośnie, aż zabraknie mi pamięci. W mojej konkretnej sytuacji nie muszę przekazywać rozwiązanych danych do mojego then(), a dane pobierają całą moją pamięć RAM.

Kod:

}).then(() => { 
    // 2. Crawl for all clanprofiles from these leaderboards 
    const promiseArray = [] 
    for (let i = 0; i < clanTags.length; i++) { 
     // Resolved data from getClanProfile() is eating up all my RAM 
     const p = backgroundScheduler.getClanProfile(clanTags[i], true) 
     promiseArray.push(p) 
    } 
    return Promise.all(promiseArray) 
    }).then(() => { 

Więc czy jest jakiś sposób, aby poczekać aż promiseArray zostanie rozwiązany bez konieczności rozwiązanych dane?

+2

58.000 obietnic ?! Brzmi dla mnie tak, jakbyś musiał mieć punkt odcięcia. Jeśli otrzymasz więcej niż X, porzuć niektóre z nich. Jeśli otrzymujesz tyle połączeń, powinieneś rozważyć pewne równoważenie obciążenia i uruchomienie serwera w więcej niż jednym procesie węzła. –

+2

Podejrzewam, że nie jest to możliwe w przypadku rodzimych obietnic, ponieważ nie mają one możliwości dowiedzenia się, że żaden z dołączonych wywołań zwrotnych nie użyje danych (dopóki sama obietnica nie zostanie przypisana do GC). –

+3

W celu uniknięcia przechowywania danych odpowiedzi, można wykonać 'promiseArray.push (p.then (() => 'success'))" To powinno upuścić dane, które obiera obietnica i nadal zwracać użyteczną obietnicę. Powinien. Jestem trochę nieostry, stąd komentarz zamiast odpowiedzi. – SethWhite

Odpowiedz

3

Użyjesz mniejszej ilości pamięci, jeśli nie masz żadnych obietnic 58k, powiązanych z nimi operacji asynchronicznych i danych wyników aktywnych jednocześnie.

Zamiast tego chcesz uruchomić X operacji na raz, a następnie, gdy jeden zakończy, możesz rozpocząć następny z nigdy więcej niż X w locie w tym samym czasie i nigdy więcej niż X obietnic w użyciu na raz.

Można eksperymentować z odpowiednią wartością X. Wartość 1 to operacje sekwencyjne, ale często można poprawić całkowity czas operacji od końca do końca, stosując wyższą wartość X. Jeśli wszystkie żądania trafiają na ten sam host , X to prawdopodobnie nie więcej niż 5-10 (ponieważ dany host nie może naprawdę zrobić wielu rzeczy naraz i prosząc go o więcej, niż może to zrobić, po prostu spowalnia).

Jeśli każde żądanie dotyczy innego hosta, może być możliwe zwiększenie wartości X. Eksperymenty dadzą optymalną wartość zarówno dla wykorzystania pamięci szczytowej, jak i ogólnej przepustowości i będą w pewnym stopniu zależeć od konkretnych okoliczności.

Bluebird's Promise.map() ma opcję współbieżności, która zrobi to za Ciebie, ale istnieje również wiele sposobów na kodowanie tylko X w locie w tym samym czasie.

Oto kilka innych przykładów kodujące zarządzania ile są w locie w czasie:

Make several requests to an API that can only handle 20 request a minute

How to execute promises in series?

unable to complete promises due to out of memory

Fire off 1,000,000 requests 100 at a time

How to make it so that I can execute say 10 promises at a time in javascript to prevent rate limits on api calls?


Jeśli nie potrzebują rozwiązane dane, można pozwolić mu być GCed prędzej zastępując go tak:

const p = backgroundScheduler.getClanProfile(clanTags[i], true).then(data => { 
     return 0;  // make resolved value just be a simple number 
        // so other data is now eligible for GC 
    }); 
    promiseArray.push(p)  

, a tutaj jest prosta implementacja że iteracje tablicę nie więcej niż X żądań w locie w tym samym czasie:

// takes an array of items and a function that returns a promise 
// runs no more than maxConcurrent requests at once 
function mapConcurrent(items, maxConcurrent, fn) { 
    let index = 0; 
    let inFlightCntr = 0; 
    let doneCntr = 0; 
    let results = new Array(items.length); 
    let stop = false; 

    return new Promise(function(resolve, reject) { 

     function runNext() { 
      let i = index; 
      ++inFlightCntr; 
      fn(items[index], index++).then(function(val) { 
       ++doneCntr; 
       --inFlightCntr; 
       results[i] = val; 
       run(); 
      }, function(err) { 
       // set flag so we don't launch any more requests 
       stop = true; 
       reject(err); 
      }); 
     } 

     function run() { 
      // launch as many as we're allowed to 
      while (!stop && inflightCntr < maxConcurrent && index < items.length) { 
       runNext(); 
      } 
      // if all are done, then resolve parent promise with results 
      if (doneCntr === items.length) { 
       resolve(results); 
      } 
     } 

     run(); 
    }); 
} 
+0

Dodano implementację, która kontroluje liczbę żądań "w locie" w tym samym czasie. – jfriend00