Как использовать Async или обещания Node / Express - PullRequest
0 голосов
/ 12 сентября 2018

Я разместил предыдущий вопрос здесь, Как исправить состояние гонки в Node.js / Express. Где моя консоль будет обновляться правильно, но моя веб-страница не обновляется

В основном я хочу знать, как заставить мой код завершить загрузку до обновления моей веб-страницы. Я слышал, что обещания или асинхронность работают, но я не могу правильно их использовать в своем коде. Я сделал простую версию своего кода ниже. В настоящее время, когда я загружаю свою страницу, моя функция погоды корректно обновляется, но мой flickr API выполняет еще две перезагрузки страницы, прежде чем отображаются ее результаты. Может кто-нибудь показать мне, как использовать Async или Promises, чтобы загрузить все мои данные и обновить страницу сразу?

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 
  });
}

Ответы [ 3 ]

0 голосов
/ 12 сентября 2018

Ваши функции weatherSearch и flickrSearch выполняются асинхронно, но по-разному.weatherSearch выполняет сетевой запрос, а затем обновляет текстовые глобальные переменные в обратном вызове.flickrSearch также выполняет сетевой запрос, но обрабатывает ответ через API Promise.

Проблема с вашим кодом экспресс-маршрута заключается в том, что он не написан для обработки асинхронного кода, который вы вызываете в weatherSearchи flickrSearch.Самый простой способ исправить это - удалить глобальные переменные, которые вы обновляете в своих функциях, и заставить их возвращать значения, которые они получают с помощью своих сетевых запросов.Вот краткий пример:

// 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}`
        })
      }
    });
  });
}

Понимание асинхронного кода в узле чрезвычайно важно!Существует множество замечательных статей об обещаниях, асинхронных вызовах и обратных вызовах, которые вы можете использовать, чтобы ознакомиться с ним.

0 голосов
/ 12 сентября 2018

Попробуйте BlueBird действительно легко использовать, и вы найдете много примеров в документации.

0 голосов
/ 12 сентября 2018

Вот частичный пример того, как вы могли бы «обещать» weatherSearch. Та же самая основная идея для другого ... было бы излишним включать их обоих.

// 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 });
      }
    });
  });
}

Основная посылка:

  • вы оборачиваете функцию обратным вызовом в Promise и вызываете функции разрешения / отклонения в соответствующих местах.
  • убедитесь, что вы вернули Обещание
  • при звонке вы можете использовать async / await, как указано выше, или вы можете использовать .then() и .catch().
  • В любом случае, я думаю, что лучше возвращать значения, как показано, а не использовать глобальные переменные или переменные в закрывающем закрытии.
  • Вы, вероятно, хотите каким-то образом отлавливать любые ошибки. Таким образом, если возникнет проблема, это не приведет к сбою вашего веб-сервера, и вы также можете отобразить или зарегистрировать ошибку, как необходимо, и отправить пользователю любую страницу с ошибкой, которую вы хотите. В моем примере показан пример try / catch, который обычно используется с async / await.
...