skip to Main Content

I have the following JS function that fetches data and put in a window.data property:


function fetchData() {
  if (window.data) {
    return Promise.resolve(window.data)
  } else {
    return fetch('http://example.com/data.js')
              .then(response => response.json())
              .then(json => window.data = json)
  }
}

The code works fine.
However, when there are two concurrent calls to that function, the first call to fetchData will fire the fetch.
Because the fetch can take some time, a second call to fetchData won’t find anything in the window.data and fire the same fetch.

Question :
How can I send an elegant pending signal to the second call and return the result as a resolved promise ?
Unless a promise-based solution is not possible, is there any another approach I can dig ?

The ideal solution should not block the second block but instead deal with it asynchronously with promises

Regards

I tried a mutex-based solution but it seemed over complicated.

2

Answers


  1. Use a helper variable
    like window.isResolved

    Login or Signup to reply.
  2. Since the result is being attached to window, and would-be caller may check that first. But more generally, return a promise from both branches of the conditional. That promise is your elegant representation of something pending. When the data is present, the resolve branch runs immediately…

    // callers should await this function
    async function fetchData() {
      if (window.data) {
        return Promise.resolve(window.data);
      } else {
        return fetch('http://example.com/data.js')
                  .then(response => response.json())
                  .then(json => window.data = json)
      }
    }
    
    
    // from anyplace in the code...
    const theData = await fetchData();
    
    // using older syntax, all callers should proceed in a `then()` closure...
    fetchData().then(theData => {
      // use the data in only in this scope
    });
    
    
    

    edit A commenter suggests that the OP might be aiming to avoid a second call to fetch. One way is to use the promise itself as a sentinel value.

    Restating (not endorsing using window, but for wherever the value is kept)…

    async function fetchData() {
      if (!window.promiseForData) {
        window.promiseForData = fetch('http://example.com/data.js')
                  .then(response => response.json())
                  .then(json => window.data = json)
      }
      return window.promiseForData;
    }
    

    Callers who await (or then) the result of this function will either get the already resolved promise (which will harmlessly resolve again to the data), or the existing deferred result without triggering another fetch.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search