skip to Main Content

I have three URLs I need to access in my code. The first two will always be accessed, but the third one might be accessed zero, one or two times. As all URLs take a lot of processing, I don’t want to access the third one if I don’t have to.

My code is something like this:

fetch('a')
.then(response => response.json() }
.then(data => {
  if (data.value == '1') {
    fetch('c')
    .then(response => response.json() }
    .then(data => console.log('through a', data));
  }
}
fetch('b')
.then(response => response.json() }
.then(data => {
  if (data.value == '1') {
    fetch('c')
    .then(response => response.json() }
    .then(data => console.log('through a', data));
  }
}

So, the result from URL ‘c’ might be needed zero, one or two times. Both a, b and c can take a while to process, so either ‘a’ or ‘b’ might be first accessing ‘c’. In case both need to return ‘c’, I don’t want a second fetch to be performed, I want the second one to return the value already fetched by the first one.

My thought was to do it like this:

const c = () => {
  return new Promise(() => {
    fetch('c')
    .then(response => response.json() };
  })
}

fetch('a')
.then(response => response.json() }
.then(data => {
  if (data.value == '1') {
    c().then(data => console.log('through a', data));
  }
}
fetch('b')
.then(response => response.json() }
.then(data => {
  if (data.value == '1') {
    c().then(data => console.log('through b', data));
  }
}

But this still causes ‘c’ to be accessed twice if both ‘a’ and ‘b’ return ‘1’.

How should I do this instead?

3

Answers


  1. Use Promise.all() to check the result of both fetches, then fetch from c if either of them returns the value 1.

    Promise.all([
        fetch('a').then(res => res.text()), 
        fetch('b').then(res => res.text())
    ]).then((result1, result2) => {
        if (result1 == "1" || result2 == "1") {
            fetch('c').then(...)
        }
    });
    
    Login or Signup to reply.
  2. Sounds like you want to lazily call "c" and at most once.

    Something like this should suffice

    let cPromise;
    
    const c = () => (cPromise ??= fetch("c").then((r) => r.json()));
    
    fetch("a")
      .then((r) => r.json())
      .then(({ value }) => {
        if (value === "1") {
          c().then((data) => {
            console.log("through a", data);
          });
        }
      });
    
    fetch("b")
      .then((r) => r.json())
      .then(({ value }) => {
        if (value === "1") {
          c().then((data) => {
            console.log("through a", data);
          });
        }
      });
    

    The Nullish coalescing assignment (??=) means cPromise will only be assigned once when c() is first called. It also means fetch("c") will only be called at most one time.

    Login or Signup to reply.
  3. The last time I experimented with this, I think what I observed is that if one fetch('c') is already pending but doesn’t have a response yet, then a second call to fetch('c') will not use the browser cache and instead start a second request (maybe do some experiments to see if this is correct). In this case, you can manually cache the c request by storing the promise like let cPromise = null and replacing all fetch('c').then() with:

    if (!cPromise) {
      cPromise = fetch('c')
    }
    cPromise.then(...)
    

    This can be nice if you’ll eventually need to do this for a bunch of different urls that could be saved in a Map. Keep in mind with this solution you’ll make one 'c' request, but both cPromise.then()‘s will execute, so only do this if that’s your desired behavior.

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