Limit calls to external api node

216 views Asked by At

I'm, in node and I have an array of obj {suggestion: 'text', rank: '2'} that I want to use to make a call to bing to get the first result on each of them.

At the moment, I have managed it using a Promise.all

await Promise.all(suggestions.map(async (s, i) => await bingWebSearch(s.suggestion.replace(/\s/g, '+'), i)))
    .then(r => {
      suggestions.map((s, i) => console.log(`
      n${i+1}. ${s.suggestion} | times suggested: ${s.rank} | url: ${s.webpage}
      `))
  })
    .catch(e => e.message)

that will call the function bingWebSearch and assign the website URL to the obj

const bingWebSearch = async (query, i) => {
 
    return await axios.get('https://api.bing.microsoft.com/v7.0/search?', {
      headers: {
        'Ocp-Apim-Subscription-Key': SUBSCRIPTION_KEY
      },
      params: {
        count: 1,
        mkt: 'en-US',
        q: query
      }
    }).then(r => {
      if (r.data.webPages) return suggestions[i].webpage = r.data.webPages.value[0].url
    }).catch(e => console.log(e.message))
  }

So basically, this will fire 30 calls to bing, but I am allowed only to do 3/second how I can I achieve it? I have tried with a setTimeOut, but using the async func is a bit tricky, so it did not work.

1

There are 1 answers

0
d3bgger On

here is my suggestion:

function delay(ms) {
  return new Promise(function (resolve) {
    setTimeout(resolve, ms);
  });
}

const bingWebSearch = (query, ms) => {
  return new Promise((resolve, reject) => {
    delay(ms).then(() => {
      axios
        .get("https://api.bing.microsoft.com/v7.0/search?", {
          headers: {
            "Ocp-Apim-Subscription-Key": SUBSCRIPTION_KEY
          },
          params: {
            count: 1,
            mkt: "en-US",
            q: query
          }
        })
        .then(r => {
          resolve(r.data.webPages.value[0].url);
        })
        .catch(e => {
          console.log(e.message);
          // this will just return an empty string as a result, if something goes wrong
          resolve("");
          // you can also reject and handle the exception inside calling for loop
          // if more advanced error handling is required
          // reject(e);
        });
    });
  });
};

async function load() {
  const requests = suggestions.map((s, i) => {
    // delay each request by 400ms to avoid hitting the limit of 3 requests per second
    const ms = i * 400;
    return bingWebSearch(s.suggestion.replace(/\s/g, "+"), ms);
  });

  const res = await Promise.all(requests);

  suggestions.forEach((s, i) => {
    s.webpage = res[i];
    console.log(`n${i + 1}. ${s.suggestion} | times suggested: ${s.rank} | url: ${s.webpage}`);
  });
}

(function () {
  load();
})();

I refactored bingWebSearch a bit, to only return the result, and not directly changing the list of suggestions. Try keeping functions as self contained as possible without external dependencies.