Reputation: 87
I posted a previous question here, How to fix a race condition in Node.js/Express. Where my console will update correctly but my webpage doesn't update
Basically i want to know how to get my code to finish loading before my webpage updates. I have heard that promises or async work but i have not be able to employ them correctly in my code. I have made a simple version of my code below. Currently when i load my page my weather function is correctly updated but my flickr API takes two more page reloads before its results are displayed. Could someone please show me how to use Async or Promises to load all my data and update the page at once?
app.get('/', function (req, res) {
// Render the webpage
res.render('index', {weather: null, headlocation: null, lat: null, long: null, imgLinks: null, WebLinks: null, imgLinksFl: null, restLat: null, restLong: null, restname: null, error: null});
})
// Main Page
app.post('/', function (req, res) {
city = req.body.city; // Grab the users input city
//console.log(weatherSort); // Debugging
weatherSearch(); // Openweather API
filckrSearch(); // Flickr API
res.render('index', {weather: weatherText, headlocation: headLocationText, lat: latLocation, long: longLocation, imgLinks: imageLinks, WebLinks: websiteLinks, imgLinksFl: imageLinksFlick, restLat: latitudeRest, restLong: longitudeRest, restname: restName, error: null});
});
// Weather function
function weatherSearch(){
// API URL
let urlw = `http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKeyWeather}`
// Send out a request
request(urlw, function (err, response, bodyW) {
// Check for errors
if(err || (JSON.parse(bodyW).cod == '404') || (JSON.parse(bodyW).cod == '401')){
// If errors are found initialize all variables to empty so that it protects from future errors
// in other API functions
} else {
let weather = JSON.parse(bodyW) // Get JSON result
weatherText = `It's ${weather.main.temp} degrees in ${weather.name}! ${weatherSort}: ${weatherInfo}`;
headLocationText = `The City of ${basicLocation}`;
}
});
}
// Flickr API
function filckrSearch(){
// Create a new Flickr Client
var flickr = new Flickr(apiKeyFlickr);
// Search Flickr based on latitude and longitude of city
flickr.photos.search({
lat: latLocation,
lon: longLocation,
radius: 20, // Set radius to 20km
sort: flickrsort // Sort the photos by users selection
}).then(function (res) {
var farmid = res.body.photos.photo[0].farm;
}).catch(function (err) {
console.error('bonk', err); // Catch errors
});
}
Upvotes: 0
Views: 208
Reputation: 1342
Both your weatherSearch
and flickrSearch
functions are executing asynchronously but in different ways. weatherSearch
is making a network request and then updating your text global variables in a callback. flickrSearch
is also making a network request but is handling the response via the Promise API.
The issue with your express route code is that it is not written to handle the asynchronous code you are calling in weatherSearch
and flickrSearch
. The simplest way to fix this is to remove the global variables you are updating in your functions, and have them return the values they retrieve with their network requests. Here is a quick example:
// Main Page
app.post('/', async function (req, res) {
const weatherResults = await weatherSearch(); // Here we 'await' the response before rendering the HTML
res.render('index', {
weather: weatherResults.weatherText,
headlocation: weatherResults.headLocationText
});
});
// Weather function
async function weatherSearch() {
let urlw = `http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKeyWeather}`
return new Promise(function (resolve, reject) {
request(url, function (error, res, body) {
if (err || (JSON.parse(bodyW).cod == '404') || (JSON.parse(bodyW).cod == '401')){
// This is how the Promise API 'returns' an error on failure
reject();
}
else {
let weather = JSON.parse(bodyW)
// This is how the Promise API 'returns' a value on success
resolve({
weatherText: `It's ${weather.main.temp} degrees in ${weather.name}! ${weatherSort}: ${weatherInfo}`,
headLocationText: `The City of ${basicLocation}`
})
}
});
});
}
Understanding async code in node is extremely important! There a lot of great articles on Promises, async await, and callbacks out there that you can use to become familiar with it.
Upvotes: 1
Reputation: 76
Give it a try to BlueBird is really easy to use and you will find a lot of examples on the documentation .
Upvotes: 0
Reputation: 7464
Here's a partial example of how you might "promisify" weatherSearch
. Same basic idea for the other one...it would be redundant to include both of them.
// Main Page
app.post('/', async function (req, res) {
city = req.body.city; // Grab the users input city
//console.log(weatherSort); // Debugging
try {
let { weatherText, headLocationText } = await weatherSearch(); // Openweather API
await filckrSearch(); // <- promisify the same as above
res.render('index', { weather: weatherText, headlocation: headLocationText, lat: latLocation, long: longLocation, imgLinks: imageLinks, WebLinks: websiteLinks, imgLinksFl: imageLinksFlick, restLat: latitudeRest, restLong: longitudeRest, restname: restName, error: null });
} catch (e) {
// do something if you get an error
}
});
// Weather function
function weatherSearch() {
// API URL
let urlw = `http://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKeyWeather}`
// Send out a request
return new Promise((resolve, reject) => {
request(urlw, function (err, response, bodyW) {
// Check for errors
if (err || (JSON.parse(bodyW).cod == '404') || (JSON.parse(bodyW).cod == '401')) {
// If errors are found initialize all variables to empty so that it protects from future errors
// in other API functions
reject(err);
} else {
let weather = JSON.parse(bodyW) // Get JSON result
weatherText = `It's ${weather.main.temp} degrees in ${weather.name}! ${weatherSort}: ${weatherInfo}`;
headLocationText = `The City of ${basicLocation}`;
resolve({ weather, weatherText, headLocationText });
}
});
});
}
Basic premise is:
async
/await
as above, or you can use .then()
and .catch()
. Upvotes: 1