如何使用异步或承诺Node / Express

时间:2018-09-11 23:34:19

标签: javascript html node.js express ejs

我在这里How to fix a race condition in Node.js/Express. Where my console will update correctly but my webpage doesn't update

发布了上一个问题

基本上,我想知道如何在网页更新之前完成代码的加载。我听说了promise或async的工作,但是我无法在我的代码中正确使用它们。我在下面做了一个简单的代码版本。目前,当我加载页面时,我的天气功能已正确更新,但是flickr API需要再​​加载两次页面才能显示其结果。有人可以告诉我如何使用异步或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 :(得分:1)

这是您如何“承诺” 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中,然后在适当的位置调用resolve / reject函数。
  • 确保您退还诺言
  • 拨打电话时,可以如上所述使用async / await,也可以使用.then().catch()
  • 无论哪种方式,我都认为最好返回显示的值,而不是在包装闭包中使用全局变量或变量。
  • 您可能想以某种方式捕获任何错误。这样,如果发生问题,它不会使您的Web服务器崩溃,并且您还可以适当地显示或记录错误,并向用户发送所需的任何错误页面。我的示例显示了try / catch,通常与async / await一起使用。

答案 1 :(得分:1)

您的weatherSearchflickrSearch函数都是异步执行的,但方式不同。 weatherSearch正在发出网络请求,然后在回调中更新文本全局变量。 flickrSearch也在发出网络请求,但正在通过Promise API处理响应。

您的快速路由代码存在问题,因为它不能编写为处理您在weatherSearchflickrSearch中调用的异步代码。解决此问题的最简单方法是删除要在函数中更新的全局变量,并让它们返回通过网络请求检索的值。这是一个简单的示例:

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

了解节点中的异步代码非常重要!有很多关于Promises,异步等待和回调的出色文章,您可以用来熟悉它。

答案 2 :(得分:0)

尝试使用BlueBird真的很容易,并且您会在文档中找到很多示例。