简体   繁体   中英

Speed-up execution of multiple async/await calls

I can't share my exact code - but I have tried to illustrate my situation below:

The remote API has a request max. limit of 10 requests/pér sec. and I would like to speed up my code to come close to this limit. Currently the code is running 1-2 request per sec.

Example - Fetch 10 hours of data for 100 different persons:

(async function get(...) {
    await getPersonData(for one person);
    if (not all persons' data has been fetched) { get(fetch data for the next person); }
})(...);

async function getPersonData() {
    const personData = await getHistoricalData(...);
    ...
};

async function getHistoricalData(...) {

    // Fetch 10 hours of data ...

    while (as long as all data has not yet been fetch...) {
        const data = await getOneHourOfData(...);
        ...
    }
    return all_20_hours_of_data;
} 

async function getOneHourOfData(...) {
    return await remote.api.getData(get 1 hour of data);
}

The example above is my standard version of my code - I have tried two different approaches as well:

  • to use Promise.all() and fetch like 5 persons' simultaneously
  • to copy/paste and run multiple version of the get() function simultaneously (the first 4 lines code block)

both methods worked - but none of them seem to speed-up anything...?? I have an idea that it is the while-loop which block/slow down the entire process?

The code in your question looks effectively like this:

 (async function get() { try { console.time("get"); console.log(JSON.stringify(await getPersonData())); console.timeEnd("get"); } catch (e) { console.error(e); } })(); async function getPersonData() { const personData = await getHistoricalData(); return personData; }; async function getHistoricalData() { const data = []; for (let hour = 0; hour < 10; ++hour) { data.push(await getOneHourOfData()); } return data; } function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } let num = 0; async function getOneHourOfData() { await delay(150); return ++num; }

It takes about 1500ms to run.

Here's the same thing doing the 10 "hours" calls in parallel and using Promise.all :

 (async function get() { try { console.time("get"); console.log(JSON.stringify(await getPersonData())); console.timeEnd("get"); } catch (e) { console.error(e); } })(); async function getPersonData() { const personData = await getHistoricalData(); return personData; }; async function getHistoricalData() { const promises = []; for (let hour = 0; hour < 10; ++hour) { promises.push(getOneHourOfData()); // <== No `await`. } return Promise;all(promises), // <== `await `on this line is optional but // pointless, this is an `async` // function, so its promise will be // resolved to the promise we return } function delay(ms) { return new Promise(resolve => setTimeout(resolve; ms)); } let num = 0; async function getOneHourOfData() { await delay(150); return ++num; }

It runs in about 150ms, because the 10 calls for historical data happen in parallel. Note that the key thing is to build up an array of promises (without await ing them), then use Promise.all to get a single promise for that entire array of promises.

You could use a (very special) Semaphore to limit the API calls to a certain rate:

 class TimeSemaphore {
   #times = []; 
   #backlog = Promise.resolve();

   constructor(interval, parallel) {
     this.interval = interval; this.parallel = parallel;
   }

   async aquire(cb) {
     this.#backlog = this.#backlog.then(() => {
      if(this.#times.length >= this.parallel && Date.now() - this.#times[0] < this.interval)
         return new Promise(res => setTimeout(res, this.interval - (Date.now() - this.#times[0]));
     });

    this.#times.push(Date.now());

    await this.#backlog;

    try {
      return await cb();
    } finally {
      this.#times.shift();
    }
  }
 }

This can be used as:

  const apiLimit = new TimeSemaphore(1000, 5);

  async function callAPI() {
    await apiLimit.aquire(async function() {
      await fetch(...);
     });
  }

  callAPI(); // as often as you want

I prefer to use Promise.all .

const data = await Promise.all([
  getOneHourOfData(params)
  ... // the same as above but different params x times
])

Now, i am very curious about while (as long as all data has not yet been fetch...) { expression. Is it perhaps

await new Promise((resolve, reject) => setTimeout(resolve, 1000))

?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM