skip to Main Content

I’m currently working on a frontend script that fetches data from an API. To optimize performance, I implemented Promises. Once an API call is made for a specific parameter, the results are stored in a variable, and subsequent requests for the same parameter are resolved using the previously fetched data.

Current code looks like this

let _knownData = [];
function callAPI(param) {
    return new Promise((resolve, reject) => {
        let find = _knownData.find(x => x.param === param);
        if (find) {
            resolve(find.response);
        } else {
            fetch (API_URL, {param: param}). then(response => {
                // process response into res and cache it
                .....
                if (res) {
                    _knownData.push({ param: param, response: res });
                    resolve(res);
                } else {
                    reject(false);
                }
            })
        }
    });
}


callAPI(param1).then( ... )
callAPI(param2).then( ... )

This works well, unless two requests for the same parameter are made in parallel. In such scenario, the API is called twice because the second request is initiated before the first one is resolved and stored in cache variable.

I want to achieve a scenario where only one API call per unique parameter is made, and any subsequent requests for the same parameter will wait for the first promise to resolve. I’m looking for a solution that doesn’t overly complicate the code.

3

Answers


  1. Store the promise instead of waiting for the final result.

    const _knownData = {};
    function callAPI(param) {
        if (_knownData[param]) return _knownData[param];
        const promise = fetch(API_URL, {param});
        _knownData[param] = promise;
        return promise;
    }
    

    Note that the if (res) logic you have in the original code is pointless. res will never not be a truthy value.

    Login or Signup to reply.
  2. You can store the promises rather than store the data in the _knownData that way it will not know when two calls with exacts params are made in smaller time frame

    let _knownData = {};
    
    function callAPI(param) {
        // in case we don't have any promise
        if (!_knownData[param]) {
            _knownData[param] = fetch(API_URL, { param: param })
                .then(res => {
                    if (res) {
                        return res;
                    } else {
                        throw new Error('API call failed');
                    }
                })
                .finally(() => {
                    // Optional: If you want to clear the promise after it resolves/rejects
                    // so that a new API call is made on a subsequent request, you can just delete it like this or 
    // use the old respone as cache.
                    // delete _knownData[param];
                });
        }
        // you are using .then so returning it will work great as well
        return _knownData[param];
    }
    
    callAPI(param1).then(...).catch(...);
    callAPI(param2).then(...).catch(...);
    Login or Signup to reply.
  3. I generally store the promise and then replace it with resolved value (I need that mainly for REPLing with it in devtools without async code)

    function isPromise(v) {
      return typeof v?.then === 'function';
    }
    const cache = new Map();
    function fetchCached(url) {
      if (cache.has(url)) {
        let result = cache.get(url);
        if (isPromise(result))
          return result; // unresolved: Promise<Data>
        else
          return result; // resolves *syncronous*: Data
      }
      const result = fetch(url); // note: it can fail
      cache.set(url, result);
      result.then(
        data => { cache.set(url, data); return data; }, // seceed update cache with Data
        err => cache.delete(url) // failed clear cache to allow retrying
      );
      return result;
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search