javascriptnode.jsasynchronouscachingasync-await

How can I improve async data retrieval and caching?


I've got the following code that retrieves data asynchronously and caches it for performance optimization purposes:

let cache = {}
const optimizedFetch = async (url) => {
    if (url in cache) {
        // return cached result if available
        console.log("cache hit")
        return cache[url]
    }
    try {
        const response = await fetch (url)
        const json = response.json();
        // cache response keyed to url
        cache[url] = json
        return json
    }
    catch (error) {
        console.log(error)
    }
}

optimizedFetch("https://jsonplaceholder.typicode.com/todos").then(console.log)

The approach above works fine but if a second request to the same url comes while the first one is still awaited then a second fetch will be fired.

How can I improve that scenario?


Solution

  • let cache = {};
    let awaits = {};
    const optimizedFetch = (url) => {
      return new Promise((resolve, reject) => {
        if (url in cache) {
          // return cached result if available
          console.log("cache hit");
          return resolve(cache[url]);
        }
        if (url in awaits) {
          console.log("awaiting");
          awaits[url].push({
            resolve,
            reject
          });
          return;
        }
        console.log("first fetch");
        awaits[url] = [{
          resolve,
          reject
        }];
        fetch(url)
          .then(response => response.json())
          .then(result => awaits[url].forEach(({
            resolve
          }) => resolve(result)))
          .catch(error => awaits[url].forEach(({
            reject
          }) => reject(error)))
          .finally(() => {
            delete awaits[url];
          });
      });
    };
    
    optimizedFetch("https://jsonplaceholder.typicode.com/todos")
      .then(({
        length
      }) => console.log(length));
    optimizedFetch("https://jsonplaceholder.typicode.com/todos")
      .then(({
        length
      }) => console.log(length));