Categories
interview

Group API calls with callbacks (asynchronous)

Write a class with a method getKey(key, callback) that invokes an API with the provided key. The method should also invoke a callback with the corresponding value returned by the API . Calls that are made within a given interval should be grouped.

Input:
getKey(v1/get?key=foo , () => console.log(x));

Output:
Let us assume the API returns the following
{foo: 50} 
Then the callback is going to be invoked with corresponding value 50
// 50

API call with grouped keys, is used when the the subsequent calls are made within a 20 ms delay for example,

Input:
getKey(v1/get?key=foo , () => console.log(x)); // at t = 0
getKey(v1/get?key=bar , () => console.log(x)); // at t = 10
getKey(v1/get?key=foo , () => console.log(x)); // at t =20

Output:
Let us assume the API (domain/get?key=foo,bar,foo) returns the following
{foo: 50, bar: 100} 
Then the callbacks are going to be invoked with the corresponding values 50, 100 and 50.
// 50
// 100
// 50
class Api {
  constructor(url, delay) {
    this.url = `${url}?key=`;
    this.delay = delay;
    this.queue = [];
    this.blocked = false;
  }

  getkey = (key, cb) => {
    this.queue.push([key, cb]);
    if (!this.blocked) {
      this.blocked = true;
      setTimeout(() => this.callApi(), this.delay);
    }
  };

  callApi = async () => {
    if (this.queue.length) {
      const keyMap = {};
      while (this.queue.length) {
        const [key, cb] = this.queue.shift();
        (keyMap[key] || (keyMap[key] = [])).push(cb);
      }
      try {
        const response = await fetch(this.url + Object.keys(keyMap).join(','));
        if (!response.ok) {
          throw 'Status: ' + response.status;
        }
        const data = await response.json();
        console.log(data)
        /*
        Testing for httpbin response.
                const data = {
                  args: {
                    key: "foo"
                  }
                };
        */
        data[data.args.key] = 12;
        for (const [key, val] of Object.entries(data)) {
          (keyMap[key] || []).forEach(cb => cb(val));
        }
      } catch (error) {
        throw error;
      }
      this.callApi();
    } else {
      this.blocked = false;
    }
  };
}

/* Test case */
const api = new Api('https://httpbin.org/get', 3000);
const test = (x) => console.log(x);
api.getkey('foo', test);

Demo