Search code examples
javascriptimplementationmemoization

How can I implement memoize method on an async function in JavaScript?


I have been trying to write the implementation of the memoize function in JavaScript. I was asked this in an interview question and haven't been able to put it out of my mind since. I would really appreciate some help with this.

Given a function in which we are making an API call -

function getSomeData(foo, callback) {
  var uri = 'http://localhost?foo=' + foo ;
  someAPIUri(uri, function onResponse(error, response, body) {
    callback(error, body);
  });
}

Instead of using an API, using setTimeout for async functionality -

function getSomeData(foo, callback) {
  setTimeout(()=> {
    console.log('async request');
    callback(2 * foo);
  }, 1000);
}

If we make a call like below twice, that means two async calls made, so we need to create a memoize function which caches the response for some input and respond with that in any subsequent call.

getSomeData(1, (response) => {
  console.log('getSomeData', response);
})

I wrote this function -

function memoize(fn) {
  const cache = {};

  return async function() {
    const args = JSON.stringify(arguments);

    console.log('arguments passed to memoize fn: ', args);
    console.log('cache data: ', cache[args]);

    cache[args] = cache[args] || fn.apply(undefined, arguments);
    return cache[args]
  }
}

const memoizedGetSomeData = memoize(getSomeData);

const callback_fn = (response) => {
  console.log('callback response: ', response);
}

memoizedGetSomeData(1, callback_fn);

memoizedGetSomeData(1, callback_fn);

This is not working as the async call is getting made for each memoizedGetSomeData call.

I would really appreciate some input to get it working and improving my understanding.

This is a codepen of the code - link

The console log:

"arguments passed to memoize fn: " "{'0':1}"
"cache data: " undefined
"arguments passed to memoize fn: " "{'0':1}"
"cache data: " undefined
"async request"
"callback response: " 2
"async request"
"callback response: " 2

Solution

  • The only real issue (from what I see) is that getSomeData is a poor mock of async functionality, as it doesn't return a promise.

    function getSomeData(foo, callback) {
      return new Promise((resolve, reject) => {
          setTimeout(()=> {
              console.log('async request');
              resolve(callback(2 * foo));
          }, 1000);
      });
    }
    
    function memoize(fn) {
      const cache = {};
      return async function() {
        const args = JSON.stringify(arguments);
    
        console.log('arguments passed to memoize fn: ', args);
        console.log('cache data: ', cache);
    
        cache[args] = cache[args] || fn.apply(undefined, arguments);
        return cache[args]
      }
    }
    
    const memoizedGetSomeData = memoize(getSomeData);
    
    const callback_fn = (response) => {
      console.log('callback response: ', response);
    }
    
    memoizedGetSomeData(1, callback_fn);
    memoizedGetSomeData(1, callback_fn);