Reputation: 2539
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:
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?
Upvotes: 3
Views: 4770
Reputation: 138567
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
Upvotes: 1
Reputation: 1075925
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.
Upvotes: 2
Reputation: 11325
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))
?
Upvotes: 0