1

I've been having a problem with the Reverse Geocode API provided by TomTom where I get 429 responses back, because I am mapping over 72 latitude and longitude objects to the API.

So I decided tried to make a function which would wait 5 seconds before firing another request; As TomTom advises 5 seconds between requests.

I thought it was totally kosher to (like in my function below) call a function, (like in my case) call Topography.getTopography(latLng) and then off of that then call, I can take that result and feed it to the TomTom request. Is this wrong? Or is it my setTimeout?

Here is my function

async function getTopographyData(latLang) {
    const retryTimes = 5;
    let counter = 0;
    var newObj = {};

    Topography.getTopography(latLang, options)
      .then((results) => {
        newObj.topography = results;
        newObj.latlng = latLang;
        return newObj;
      })
      .then(async (newObj) => {
        var { lat, lng } = newObj.latlng;

        let result = await axios.get(
          `https://api.tomtom.com/search/2/reverseGeocode/crossStreet/${lat},${lng}.json?limit=1&spatialKeys=false&radius=10000&allowFreeformNewLine=false&view=Unified&key=${process.env.TOM_TOM_API}`
        );

        var { addresses } = result?.data;
        var { address, position } = addresses[0];

        var [lat, lng] = position.split(",").map((p) => +p);

        newObj = {
          ...newObj,
          latlng: { lat, lng },
          address,
        };

        dispatch({ type: "setTopographyData", payload: newObj });
      })
      .catch(function (error) {
        if (
          (error.response?.status == 403 || error.response?.status == 429) &&
          counter < retryTimes
        ) {
          counter++;
          return new Promise(function (resolve, reject) {
            setTimeout(function () {
              resolve(getTopographyData(counter));
            }, 5000);
          });
        } else {
          console.log("error", error);
        }
      });
  }

Any help would be appreciated.

Update while num8er's solution works, I believe there must be a problem with TomTom as it stops at 45 (getting a reverseGeoCode look up for 45 markers latlang objects) and spits back errors in the console. Furthermore when I click the link in the console I can see the response in the browser?!

enter image description here

enter image description here

Antonio Pavicevac-Ortiz
  • 7,239
  • 17
  • 68
  • 141
  • `getTopographyData(counter)` - how does `counter` become `latLang`? Overall though I'd consider rewriting this, implementing retry for specific fetcher function instead, isolating this logic from the rest of component. – raina77ow Aug 19 '22 at 15:50
  • @raina77ow OMG i didn't notice I had done that passing `counter` instead of `latLang` facepalm— thank you. But I'll try to do what you suggest... – Antonio Pavicevac-Ortiz Aug 19 '22 at 15:56

1 Answers1

1

How about refactoring it with regular loop with pause and break to make code more readable:

async function getTopographyData(latLng) {
    const retryTimes = 5;
    const topographyData = {
      latlng: latLng,
      address: null,
    };

    const pause = (ms) => new Promise(resolve => setTimeout(resolve, ms));

    for (let counter = 1, canRetry = false; counter <= retryTimes; counter++) {
      try {
        const topography = await Topography.getTopography(latLng, options);
        topographyData.topography = topography;
       
        const { lat, lng } = topographyData.latlng;
        const response = await axios.get(
          `https://api.tomtom.com/search/2/reverseGeocode/crossStreet/${lat},${lng}.json?limit=1&spatialKeys=false&radius=10000&allowFreeformNewLine=false&view=Unified&key=${process.env.TOM_TOM_API}`
        );
        
        if (response?.data) {
          const { addresses } = response.data;
          const { address, position } = addresses[0];
          
          const [lat, lng] = position.split(",").map((p) => +p);
          
          topographyData.address = address;
          topographyData.latlng = {lat, lng};
        }
        
        dispatch({ type: "setTopographyData", payload: topographyData });
      }
      catch (error) {
        console.error(error);
        
        canRetry = [403, 429].includes(error.response?.status);
      }
      
      if (!canRetry) {
        break;
      }
      
      await pause(5000);      
    }
  }
num8er
  • 18,604
  • 3
  • 43
  • 57
  • 1
    Looks good to me; I'd consider moving dispatch outside of the loop (or even extracting the whole TomTom API call away alongside the reply; after all, this wrapping logic might be useful elsewhere), but that might be actually less readable... It's funny that somehow I thought that `getTopography` response is stable, but the original code indeed called that func in the loop as well. – raina77ow Aug 19 '22 at 16:49
  • 1
    Thank you so much. While this works, I believe there must be a problem with TomTom as it stops at 45 (getting a reverseGeoCode look up for 45 markers latlang objects) and spits back errors in the console. Furthermore when I click the link in the console I can see the response in the browser?! – Antonio Pavicevac-Ortiz Aug 19 '22 at 17:23
  • @AntonioPavicevac-Ortiz can You write me what is in error message? – num8er Aug 19 '22 at 17:42