Nikhil
Nikhil

Reputation: 2318

Nodejs: How to use async/await when trying to rate limit number of requests to the server

I am trying to use the geocode API to get the json object by limiting the number of requests to 1 per second. I need to use async/await and this is what I have

const requester = {
    lastRequest: new Date(),
    makeRequest: async (url) => {
        var timestart = new Date()
        // first check when last request was made
        var timeSinceLast = (timestart).getTime() - this.lastRequest.getTime();
        if (timeSinceLast < 1000) {
            await new Promise(resolve => setTimeout(resolve, timeSinceLast));
        }
        const response = await fetch(url);
        const json = await response.json();
        return json;
        // make request here and return result
    }
};
var requestResponse = requester.makeRequest('https://geocode.xyz/?
     locate=Warsaw,Poland&json=1')

With this I am getting the following errors:

UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'getTime' of undefined

UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)

I tried to find why this is happening and when just print the requester object, I don't get the gettime() undefined error. Am I using async/await and promises correctly? What am I doing wrong?

EDIT: Based on the answers below, I no longer see the error, however I see 'Request Throttled'

const requester = {
   lastRequest: new Date(),
   makeRequest: async function(url){
       var timestart = new Date()
       // first check when last request was made
       var timeSinceLast = (timestart).getTime() - this.lastRequest.getTime();
       if (timeSinceLast < 1000) {
           await new Promise(resolve => setTimeout(resolve, timeSinceLast));
       }
       const response = await fetch(url);
       const json = await response.json();
       return json;
       // make request here and return result
   }
};
requester.makeRequest('https://geocode.xyz/?locate=Warsaw,Poland&json=1') 
.then(console.log) 

This is what I am seeing now:

{ success: false,
 error: { code: '006', message: 'Request Throttled.' } }

Do I need to await for the response?

Upvotes: 3

Views: 2415

Answers (3)

Krzysztof Krzeszewski
Krzysztof Krzeszewski

Reputation: 6778

According to geocode pricing options

Throttled API access is free. Rate limit: up to 1 API call per sec. API (Throttled to no more that 1 request per second for all free port users combined. eg., if 2 free requests come at the same time, each gets throttled to 2 seconds per request).

SO this would mean that as a free user you don't have control whether the limit would be 1 second or something else.

Upvotes: 1

FZs
FZs

Reputation: 18639

Arrow functions don't have this, so this.lastrequest.getTime is undefined.

Use a normal function instead:

const requester = {
    lastRequest: new Date(),
    makeRequest: async function(url){
        var timestart = new Date()
        // first check when last request was made
        var timeSinceLast = (timestart).getTime() - this.lastRequest.getTime();
        if (timeSinceLast < 1000) {
            await new Promise(resolve => setTimeout(resolve, timeSinceLast));
        }
        const response = await fetch(url);
        const json = await response.json();
        return json;
        // make request here and return result
    }
};
requester.makeRequest('https://geocode.xyz/?locate=Warsaw,Poland&json=1') //Don't write strings in two lines
.then(console.log)

And if you add a then(console.log), you can see the result!

Upvotes: 3

Amardeep Bhowmick
Amardeep Bhowmick

Reputation: 16908

Don't use => arrow functions inside object methods as the this is not what you think it is.

The this within the arrow function is the one that was in the current context where you defined the object, in your case it is pointing to the global object which has no lastRequest property.

That is why this.lastRequest is evaluating to undefined.

You need to replace it with an anonymous function to correctly point to the this.lastRequest property.

Also you won't get the data you want by simply returning it from the object method and assigning it to a variable as the value returned from your method is wrapped in a promise (due to the async keyword) so you need to call then on it to actually capture the data from the service.

const requester = {
    lastRequest: new Date(),
    makeRequest: async function(url) {
        var timestart = new Date();
        // first check when last request was made
        var timeSinceLast = timestart.getTime() - this.lastRequest.getTime();
        if (timeSinceLast < 1000) {
            await new Promise(resolve => setTimeout(resolve, timeSinceLast));
        }
        const response = await fetch(url);
        const json = await response.json();
        return json; //wrapped in a promise
        // make request here and return result
    }
};
var requestResponse = requester.makeRequest('https://geocode.xyz/?locate=Warsaw,Poland&json=1').then((data)=>{
   console.log(data);
})

Some resources to look at:

Arrow functions as object methods

Async function

Upvotes: 2

Related Questions