1

I am trying to create a weather report application using vanilla JS and APIs. My goals are:

  1. When web page finishes loading I want to ask user for their location.
  2. If the user accepts the request a. I want to get their coordinates using browser api and store the latitude and longitude and then run another async function which will be fetching data from OpenWeather api using those latitute and longitude data. b. Update the UI accordingly.
  3. If the user blocks the request: a. I want to still fetch data from OpenWeather api but this time using default values of latitude and longitude (for e.g Paris)

The problems I'm facing are:

  1. I'm trying to get geolocation using a promisified function. So this is will be executed in async function if using async/await but I am unable to run two async functions one after another finishes executing so that it can use previously executed function's response data.
  2. If user block the location in the browser the function will be rejected and the catch block will be executed, so should I place the weather api function in the catch block to use the default latitude and longitude value or do something else?
  3. As much I know, executing more than one async functions serially have no gaurantee that result will come in the same order, so sometimes weather api functions successfully fetches data from api using default lat and lon values instead of waiting for the user's location. How can I fix the code so that I can get desired output?

I'm new to JS and SO, so if any clarification is needed let me know. By the way I'm also sharing the code for better understanding. Sorry for such a long post :(

const WEATHER_API_KEY = "f19d6e315954bfb123e794ab55aa28c4";

const state = {
    location: {
        //setting default location of paris
        lat: 48.8566969,
        lng: 2.3514616
    }
};

///////////////////////////////////////
// Geolocation api function
const options = {
    enableHighAccuracy: true,
    timeout: 5000,
    maximumAge: 0
};

// Getting location using geolocation API
const getPosition = function () {
    return new Promise(function (resolve, reject) {
        navigator.geolocation.getCurrentPosition(resolve, reject, options);
    });
};

//////////////////////////////////////////////
// Helper function to fetch data from api
const TIMEOUT_SEC = 5;

const timeout = function (s) {
    return new Promise(function (_, reject) {
        setTimeout(function () {
            reject(new Error(`Request took too long! Timeout after ${s} second`));
        }, s * 1000);
    });
};

//get json data from api
export const AJAX = async function (url, method = undefined) {
    try {
        const fetchPro = method
            ? fetch(url, {
                    method: "get"
              })
            : fetch(url);

        const res = await Promise.race([fetchPro, timeout(TIMEOUT_SEC)]);
        const data = await res.json();

        if (!res.ok) throw new Error(`${data.message} (${res.status})`);
        return data;
    } catch (err) {
        throw err;
    }
};

//////////////////////////////
// Executing functions and fetching data

const loadData = async function () {
    try {
        const { coords } = await getPosition();
        const { latitude, longitude } = coords;
        state.location.lat = latitude;
        state.location.lng = longitude;
        const weatherData = await AJAX(
            `https://api.openweathermap.org/data/2.5/onecall?lat=${state.location.lat}&lon=${state.location.lng}&&units=metric&appid=${WEATHER_API_KEY}`
        );
        console.log(weatherData);
    } catch (err) {
        console.error("Error occured!!", err);
    }
};

window.addEventListener("load", loadData);
ikhvjs
  • 5,316
  • 2
  • 13
  • 36
halbmond
  • 29
  • 1
  • 5
  • 1
    um, call it when it is done? – epascarello Aug 26 '21 at 13:41
  • So you are saying is `const { coords } = await getPosition();` is not awaiting the response? – epascarello Aug 26 '21 at 13:44
  • I remember this exercise (*not* that long ago) as the one in which I started to understand the concept of callbacks. In this case, working with Promises, make sure you understand about `.then`. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises https://masteringjs.io/tutorials/fundamentals/then – Andy Bonner Aug 26 '21 at 13:46
  • Yes. I want to fetch data in either of the cases, after getting user's location or in case of any error occured while getting user's location. – halbmond Aug 26 '21 at 13:46
  • `const fetchPro = method ? fetch(url, { method: "get" }) : fetch(url);` <-- FYI your code is wrong here ;) You do not use method.... – epascarello Aug 26 '21 at 13:51
  • @epascarello Actually this is a part of entire code structure where some api calls required method and some do not. So in this case there is no use of method. The code is fine and running properly – halbmond Aug 26 '21 at 13:56
  • @GargiChaurasia so you use `method` as a boolean to set "get" when the default of fetch is get? – epascarello Aug 26 '21 at 13:59
  • @epascarello yeah.. – halbmond Aug 26 '21 at 14:06
  • `fetch(url)` and `fetch(url, {method: 'GET'})` are the same thing.... – epascarello Aug 26 '21 at 14:31
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/236452/discussion-between-gargi-chaurasia-and-epascarello). – halbmond Aug 26 '21 at 15:50

1 Answers1

1

You can catch the error in the getPosition() and return default state.

const WEATHER_API_KEY = "f19d6e315954bfb123e794ab55aa28c4";

const state = {
  location: {
    //setting default location of paris
    latitude: 48.8566969,
    longitude: 2.3514616,
  },
};

///////////////////////////////////////
// Geolocation api function
const options = {
  enableHighAccuracy: true,
  timeout: 5000,
  maximumAge: 0,
};

// Getting location using geolocation API
const getPosition = async function () {
  try {
    const { coords } = await new Promise(function (resolve, reject) {
      navigator.geolocation.getCurrentPosition(resolve, reject, options);
    });
    return coords;
  } catch (err) {
    console.log(err);
    return state.location;
  }
};

//////////////////////////////////////////////
// Helper function to fetch data from api
const TIMEOUT_SEC = 5;

const timeout = function (s) {
  return new Promise(function (_, reject) {
    setTimeout(function () {
      reject(new Error(`Request took too long! Timeout after ${s} second`));
    }, s * 1000);
  });
};

//get json data from api
export const AJAX = async function (url, method = undefined) {
  try {
    const fetchPro = method
      ? fetch(url, {
          method: "get",
        })
      : fetch(url);

    const res = await Promise.race([fetchPro, timeout(TIMEOUT_SEC)]);
    const data = await res.json();

    if (!res.ok) throw new Error(`${data.message} (${res.status})`);
    return data;
  } catch (err) {
    throw err;
  }
};

//////////////////////////////
// Executing functions and fetching data

const loadData = async function () {
  try {
    const { latitude, longitude } = await getPosition();
    const weatherData = await AJAX(
      `https://api.openweathermap.org/data/2.5/onecall?lat=${latitude}&lon=${longitude}&&units=metric&appid=${WEATHER_API_KEY}`
    );
    console.log(weatherData);
  } catch (err) {
    console.error("Error occured!!", err);
  }
};

window.addEventListener("load", loadData);

Edit for explanation:

async/await below:

async function run() {
  try {
    const { latitude, longitude } = await getPosition();
    const weatherData = await AJAX(
      `https://api.openweathermap.org/data/2.5/onecall?lat=${latitude}&lon=${longitude}&&units=metric&appid=${WEATHER_API_KEY}`
    );
    console.log(weatherData);
  } catch (err) {
    console.log(err);
  }
}

can be written as

getPosition()
  .then(({ latitude, longitude }) => {
    AJAX(`lat=${latitude}&lon=${longitude}`).then(weatherData => {
      console.log(weatherData);
    });
  })
  .catch(console.log);

That's why getPosition() will executed before AJAX().

ikhvjs
  • 5,316
  • 2
  • 13
  • 36
  • This helped but I have a doubt that whether AJAX func() will wait for getPosition() to return response? I'm bit confused now how these two async functions will be executed and in which order. Will you please help me understand this? – halbmond Aug 26 '21 at 15:57
  • @GargiChaurasia, I add my explanation in the answer. – ikhvjs Aug 26 '21 at 18:08