2

Following is what I'm trying to do this in nodejs. The Rest API takes a city name as an input. I am trying to get the latitude and longitude using the geocode API for the input city. then, using the latitude and longitude, I am trying to get a list of closest cities using another API. then, for all those cities, I am getting the weather report, then for those cities, I am getting whether there is water and I am returning this back as a JSON.

As you can see, there is a lot of then and the goal of this exercise is to avoid nested callbacks.

I am using async/await which is supposed to have eliminated the nested then functions. But I don't see another way of doing this. The complete code snippet is below. The ugly part I am trying to fix is requester.makeRequest()

Following is just a snippet of the necessary code and not the complete working code. Any help on how to untangle this would be greatly appreciated.

app.get('/search', function(req, res, next) {
  const requester = {
      lastRequest: new Date(),
      makeRequest: async function(url) {
        const response = await fetch(url);
        const json = await response.json();
        return json;
      }
  };

requester.makeRequest(geocode_url +`?locate=${req.query.q}&json=1`
    + geocode_token)
  .then(function(city){
    var final_result = []
    var lat = city.latt;
    var long = city.longt;
    // request to get list of cities closer to that location,
    //takes latitude and longitude as parameters
    requester.makeRequest(metaweather_url + '?lattlong='
     + lat + ',' + long)
    .then(function(closer_cities) {
      var cities_len = closer_cities.length
      for(i = 0; i < closer_cities.length; i++) {
        woeid = closer_cities[i].woeid
        //request to get weather using woeid parameter
        requester.makeRequest(woeid_url + woeid)
        .then(function(weather) {
          var lattlong = weather.latt_long;
          requester.makeRequest(onwater_url+ lattlong +
          '?access_token=' + water_access_token)
          .then(function(onwater) {
            var temp = Object.assign(weather, onwater)
            final_result.push(temp)
            if (final_result.length == cities_len) {
              res.status(200).json({error: false,
                data: {message: final_result}})
            }
          })
        })
       }
      })
    })
  })
3
  • If you're using the node-requester package, it has been deprecated for request-promise, which would easily allow you to use async/await as well. See deprecation notice here: github.com/icodeforlove/node-requester Commented Feb 7, 2019 at 14:45
  • What have you tried to replace each then call with an equivalent await? Commented Feb 7, 2019 at 14:50
  • you have a typo in await citi.json(), should be await city.json() Commented Feb 7, 2019 at 15:22

4 Answers 4

2

I would say you still need one then

requester.makeRequest(geocode_url +`?locate=${req.query.q}&json=1`
    + geocode_token)
  .then(async function(city){
    var final_result = []
    var lat = city.latt;
    var long = city.longt;
    // request to get list of cities closer to that location,
    //takes latitude and longitude as parameters
    closer_cities = await requester.makeRequest(metaweather_url + '?lattlong='+ lat + ',' + long);
    var cities_len = closer_cities.length;
    for(i = 0; i < closer_cities.length; i++) {
      woeid = closer_cities[i].woeid
      //request to get weather using woeid parameter
      weather = await requester.makeRequest(woeid_url + woeid)
      var lattlong = weather.latt_long;
      onwater = await awaitrequester.makeRequest(onwater_url+ lattlong + '?access_token=' + water_access_token)
      var temp = Object.assign(weather, onwater)
      final_result.push(temp)
      if (final_result.length == cities_len) {
        res.status(200).json({error: false, data: {message: final_result}})
      }
    }
  })

Edit: I don't really think my answer is relevant for your problem sorry

Sign up to request clarification or add additional context in comments.

Comments

1

for this line : requester.makeRequest ... .then(function(city){

replace .then(function(city){ with var city = await requester.makeRequest , city will have the fulfilled value of the promise, do this for the rest of thens :

( keep in mind that await is only used inside an async function, you can use an iife )

(async () => {


  var city = await requester.makeRequest(`${geocode_url}?locate=${req.query.q}&json=1${geocode_token}`);

  var final_result = []
  var lat = city.latt;
  var long = city.longt;
  // request to get list of cities closer to that location,
  //takes latitude and longitude as parameters
  var closer_cities = await requester.makeRequest(`${metaweather_url}?lattlong=${lat},${long}`);

  var cities_len = closer_cities.length;

  for (i = 0; i < closer_cities.length; i++) {
    woeid = closer_cities[i].woeid
    //request to get weather using woeid parameter
    var weather = await requester.makeRequest(woeid_url + woeid);

    var lattlong = weather.latt_long;
    var onwater = await requester.makeRequest(`${onwater_url}${lattlong}?access_token=${water_access_token}`);

    var temp = Object.assign(weather, onwater)
    final_result.push(temp)
    if (final_result.length == cities_len) {
      res.status(200).json({
        error: false,
        data: {
          message: final_result
        }
      })
    }
  }

})();

Comments

1

then is misused in the first place because it results in callback hell. Promises are callback-based but they support chaining which is supposed to eliminate nested callbacks.

It should be:

  requester.makeRequest(geocode_url +`?locate=${req.query.q}&json=1` + geocode_token)
  .then(function(city){
    var final_result = []
    var lat = city.latt;
    var long = city.longt;

    return requester.makeRequest(metaweather_url + '?lattlong='
     + lat + ',' + long)
  })
  .then(function(closer_cities) {
     ...
  });

If there's a promise inside then, it should be returned. This way there's no more than a single level of callback nesting.

await is syntactic sugar for then, and rejections should be handled as well:

app.get('/search', function(req, res, next) {
  try {
    ...
    const city = await requester.makeRequest(geocode_url +`?locate=${req.query.q}&json=1`
      + geocode_token);
    var final_result = []
    var lat = city.latt;
    var long = city.longt;

    const closer_cities = await requester.makeRequest(metaweather_url + '?lattlong='
         + lat + ',' + long);
    ...
  } catch (err) {
    next(err)
  }
});

Comments

1

When calling async functions you are not supposed to use .then(...) construct...
Simply let result = await myAsynchronousFunction(a, b, c); ...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.