Reputation: 2236
Apologize in advance about the ambiguous title.
To better explain the question - I have two functions in a fetch-form-data.js
that look something like...
let cachedBaseUrl;
const getBaseUrl = () => {
if (cachedBaseUrl) return Promise.resolve(cachedBaseUrl)
return fetch('https://some-baseurl-endpoint.com')
.then(baseUrl => {
cachedBaseUrl = baseUrl
return baseUrl
});
}
const getFormData = formId =>
getBaseUrl()
.then(baseUrl => fetch('https://form-data-endpoint.com'))
module.exports = {
getFormData
}
getFormData
is exported and called hundreds of times in a loop elsewhere. The problem is getFormData
depends on getBaseUrl
, so calling getFormData
hundreds of times also calls getBaseUrl
hundreds of times, which frequently errors because I'm guessing the baseUrl endpoint doesn't like getting hit so rapidly.
I would like to instead ensure that getBaseUrl
only gets hit once, and have all subsequent getFormData
calls "wait" on getBaseUrl
resolving before moving on.
One solution would be to export both getBaseUrl
and getFormData
wherever getFormData
is being used, await getBaseUrl
, then call getFormData
however many times as needed. Something like...
async () => {
const baseUrl = await getBaseUrl();
const formData = [];
for (let form of forms) {
formData.push(getFormData(form));
}
await Promise.all(formData);
...
}
However, the usage of getFormData
is spread across the codebase. It would be nice to simply abstract away getBaseUrl
as is the current implementation and only expose getFormData
, but solve the issue of "awaiting" all subsequent getFormData
calls until at least the first getBaseUrl
call is resolved.
My feeble attempt is to cache baseUrl, as seen in the above codeblock, but that doesn't "stop" immediate subsequent getBaseUrl
calls from occurring, which calls another fetch
because nothing is cached yet.
How would I go about implementing something like this?
Upvotes: 2
Views: 176
Reputation: 185
Just cache your promise also.
let cachedBaseUrl, cachedPromise;
const getBaseUrl = () => {
if (cachedBaseUrl) return Promise.resolve(cachedBaseUrl)
if (cachedPromise) return cachedPromise;
cachedPromise = fetch('https://some-baseurl-endpoint.com')
.then(baseUrl => {
cachedBaseUrl = baseUrl
return baseUrl
});
return cachedPromise;
}
const getFormData = formId =>
getBaseUrl()
.then(baseUrl => fetch('https://form-data-endpoint.com'))
module.exports = {
getFormData
}
Upvotes: 1
Reputation: 13853
Try this
let cachedBaseUrl;
const getBaseUrl = () => {
if (cachedBaseUrl) return cachedBaseUrl
return cachedBaseUrl = new Promise((resolve, reject) =>
fetch('https://some-baseurl-endpoint.com')
.then(BaseUrl => { cachedBaseUrl = undefined; resolve(BaseUrl) })
.catch(() => { cachedBaseUrl = undefined; reject() })
);
}
You need to stop getBaseUrl()
from sending other requests before the first request completes. So you need a Promise of that request, not a Promise.resolve(constant)
.
After request completes you set its result with resolve/reject
and reset cachedBaseUrl
, so you can pass the if
and try again.
Reset logic is up to you. You can deside to fetch baseurl-endpoint constantly but sequentially (as it is done right now), or from time to time, then you will have to call SetTimeout(interval, () => cachedBaseUrl = undefined)
instead of just cachedBaseUrl = undefined
. Or do what ever you like.
Upvotes: 3
Reputation: 11090
Your caching solution is a good start, by caching the pending promise as well I think it solves the problem elegantly.
let cachedBaseUrl;
const getBaseUrl = () => {
if (cachedBaseUrl) return Promise.resolve(cachedBaseUrl)
return cachedBaseUrl = fetch('https://some-baseurl-endpoint.com');
}
const getFormData = formId =>
getBaseUrl()
.then(baseUrl => fetch('https://form-data-endpoint.com'))
module.exports = {
getFormData
}
Upvotes: 0
Reputation: 6718
Call getBaseUrl()
once and save the promise that you get from it. Then in getFormData()
, call then
on that saved promise.
const getBaseUrl = () => ...
const baseUrlPromise = getBaseUrl()
const getFormData = formId =>
baseUrlPromise
.then(...)
It's okay to use .then(...)
multiple times on one promise to make multiple promises that depend on it.
If there's nothing else using cachedBaseUrl
, you can remove it, because the resolved value will be saved as part of baseUrlPromise
after it's resolved.
Upvotes: 0