简体   繁体   中英

Wait for 1 promise before all other promises

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?

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.

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
}

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.

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
}

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