HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Using JavaScript promises to display weather and location information

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
javascriptpromisesinformationusinganddisplaylocationweather

Problem

This is the first time I'm trying to use promises in JavaScript so I'm not sure if I have done it correctly, but it seems to work. The problem is that I have ended up with a "nested when" and it doesn't look that great. There probably is a nicer solution out there. I would really appreciate some feedback on how I can improve my code.

I am using the navigator.geolocation to get the position and then I'm using two different APIs to get the current weather and the address of the location.

What I would like it to look like would be something like:

$.when(getPosition())

.then(getWeather(position),getLocationName(position))

.done(displayWeather);


But it doesn't seem to work with two calls in then() like you can have in when().

This is my current solution:

var displayWeather = function(weatherData, locationData) {

  $('#userLocation').text(locationData.results[3].formatted_address);

  $('#weatherDescription').text(weatherData.currently.temperature);

  $('#temperature').text(weatherData.currently.summary);

}

var getPosition = function() {

  var deferred = $.Deferred();

  navigator.geolocation.getCurrentPosition(deferred.resolve, deferred.reject);

  return deferred.promise();

};

var getWeather = function(position) {

  return $.getJSON('https://api.forecast.io/forecast/{APIKEY}/' + position.coords.latitude + ',' + position.coords.longitude + '?callback=?')
     .then(function(data) {
        return data;
    });

}

var getLocationName = function(position) {

  return $.getJSON('https://maps.googleapis.com/maps/api/geocode/json?latlng=' + position.coords.latitude + ',' + position.coords.longitude + '&key={APIKEY}')
    .then(function(data) {
        return data;
  });

}

$(document).ready(function() {

  $.when(getPosition())

    .done(function(position) {

      $.when(getWeather(position), getLocationName(position)).done(displayWeather);

    });

});

Solution

$.when(getPosition())


$.when is only used when listening to multiple promises. Since getPosition is just one and it already returns a promise, $.when isn't required and you can simply chain then to getPosition().

var getWeather = function(position) {

  return $.getJSON('https://api.forecast.io/forecast/{APIKEY}/' + position.coords.latitude + ',' + position.coords.longitude + '?callback=?')
     .then(function(data) {
        return data;
    });

}

var getLocationName = function(position) {

  return $.getJSON('https://maps.googleapis.com/maps/api/geocode/json?latlng=' + position.coords.latitude + ',' + position.coords.longitude + '&key={APIKEY}')
    .then(function(data) {
        return data;
  });

}


Attaching then which just returns the resolved data data is unnecessary. $.getJSON already returns a promise-like object that resolves to data. Any then attached to it will get data in the same way this then receives it.

Additionally, suggesting you use then instead of the jQuery-specific done. then is standard, and allows you to easily move over to the standard Promises.

I usually recommend pushing off the "flow logic" of promises to the caller instead of spreading it across different functions. This allows you to easily write functions as promise-returning operations while keeping all the "flow logic" in one place.

On other stuff, suggesting you move the url into a variable. This keeps your AJAX operation short and free from the string concatenation madness. A;so recommending using named function instead of function expressions. They're easier to debug as their names appear in the stack trace.

Your code could be simplified into:

function getPosition () {

  return $.Deferred(function(deferred){
    navigator.geolocation.getCurrentPosition(deferred.resolve, deferred.reject);
  }).promise();

};

function getWeather (position) {
  var url = 'https://api.forecast.io/forecast/{APIKEY}/' + position.coords.latitude + ',' + position.coords.longitude + '?callback=?'
  return $.getJSON(url);
}

function getLocationName (position) {
  var url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng=' + position.coords.latitude + ',' + position.coords.longitude + '&key={APIKEY}';
  return $.getJSON(url);
}

$(document).ready(function() {

  getPosition().then(function(position){

    return $.when(getWeather(position), getLocationName(position));

  }).then(function(weatherData, locationData){

    $('#userLocation').text(locationData.results[3].formatted_address);
    $('#weatherDescription').text(weatherData.currently.temperature);
    $('#temperature').text(weatherData.currently.summary);

  });

});


If you can write using ES6, the code becomes simpler and you can drop the jQuery dependency for the data gathering functions. ES6 now has native promises as well as template strings, and modern browsers have fetch. This reduces jQuery footprint to only the DOM-interacting operations.

function getPosition() {
  return new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject));
};

function getWeather(position) {
  const url = 'https://api.forecast.io/forecast/${APIKEY}/${position.coords.latitude},${position.coords.longitude}?callback=?';
  return fetch(url).then(response => response.json());
}

function getLocationName(position) {
  const url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng=${position.coords.latitude},${position.coords.longitude}&key=${APIKEY}';
  return fetch(url).then(response => response.json());
}

$(document).ready(function() {

  getPosition()
    .then(position => Promise.all(getWeather(position), getLocationName(position)))
    .then(function(data){
      const weatherData = data[0];
      const locationData = data[1];

      $('#userLocation').text(locationData.results[3].formatted_address);
      $('#weatherDescription').text(weatherData.currently.temperature);
      $('#temperature').text(weatherData.currently.summary);
    });

});

Code Snippets

$.when(getPosition())
var getWeather = function(position) {

  return $.getJSON('https://api.forecast.io/forecast/{APIKEY}/' + position.coords.latitude + ',' + position.coords.longitude + '?callback=?')
     .then(function(data) {
        return data;
    });

}


var getLocationName = function(position) {

  return $.getJSON('https://maps.googleapis.com/maps/api/geocode/json?latlng=' + position.coords.latitude + ',' + position.coords.longitude + '&key={APIKEY}')
    .then(function(data) {
        return data;
  });

}
function getPosition () {

  return $.Deferred(function(deferred){
    navigator.geolocation.getCurrentPosition(deferred.resolve, deferred.reject);
  }).promise();

};


function getWeather (position) {
  var url = 'https://api.forecast.io/forecast/{APIKEY}/' + position.coords.latitude + ',' + position.coords.longitude + '?callback=?'
  return $.getJSON(url);
}


function getLocationName (position) {
  var url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng=' + position.coords.latitude + ',' + position.coords.longitude + '&key={APIKEY}';
  return $.getJSON(url);
}


$(document).ready(function() {

  getPosition().then(function(position){

    return $.when(getWeather(position), getLocationName(position));

  }).then(function(weatherData, locationData){

    $('#userLocation').text(locationData.results[3].formatted_address);
    $('#weatherDescription').text(weatherData.currently.temperature);
    $('#temperature').text(weatherData.currently.summary);

  });

});
function getPosition() {
  return new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject));
};


function getWeather(position) {
  const url = 'https://api.forecast.io/forecast/${APIKEY}/${position.coords.latitude},${position.coords.longitude}?callback=?';
  return fetch(url).then(response => response.json());
}


function getLocationName(position) {
  const url = 'https://maps.googleapis.com/maps/api/geocode/json?latlng=${position.coords.latitude},${position.coords.longitude}&key=${APIKEY}';
  return fetch(url).then(response => response.json());
}


$(document).ready(function() {

  getPosition()
    .then(position => Promise.all(getWeather(position), getLocationName(position)))
    .then(function(data){
      const weatherData = data[0];
      const locationData = data[1];

      $('#userLocation').text(locationData.results[3].formatted_address);
      $('#weatherDescription').text(weatherData.currently.temperature);
      $('#temperature').text(weatherData.currently.summary);
    });

});

Context

StackExchange Code Review Q#129544, answer score: 4

Revisions (0)

No revisions yet.