0

I am trying to make a simple weather application based on Node.js, like this one. My problem is that every mechanism I see is based on promises, and I don't understand the concept.

So, the code I see everywhere is like:

yrno.getWeather(LOCATION).then((weather) => {
    weather.getFiveDaySummary().then((data) => console.log('five day summary', data)); 
    weather.getForecastForTime(new Date()).then((data) => console.log('current weather', data));
    })
    .catch((e) => {
        console.log('an error occurred!', e);
    });

However, I was unable to find a way to resolve these promises and save the five day summary to a variable for later use.

How do I proceed?

Thanks, Robin

guest271314
  • 1
  • 15
  • 104
  • 177
Robin Molnar
  • 137
  • 1
  • 1
  • 11
  • You should look up closures. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures – christo8989 Apr 29 '17 at 17:22
  • Does `data` at last `.then()` function contain the data that you are trying to store? – guest271314 Apr 29 '17 at 17:24
  • You don't save the summary, you save the promise. When you want to use it, use the `then` method to wait for it and get passed the value as an argument. – Bergi Apr 29 '17 at 17:24
  • 4
    I guess you start with [understanding the concept](http://stackoverflow.com/a/22562045/1048572) – Bergi Apr 29 '17 at 17:25
  • Possible duplicate of [Aren't promises just callbacks?](http://stackoverflow.com/questions/22539815/arent-promises-just-callbacks) – Meirion Hughes Apr 29 '17 at 17:30
  • @guest271314 yes it does. – Robin Molnar Apr 29 '17 at 17:59
  • @MeirionHughes How does linked Question resolve present Question? – guest271314 Apr 29 '17 at 18:13
  • @guest271314 The answer in the linked question answers "How do I resolve a promise in Node.js?" and "How to save the variable returned from a Promise"... – Meirion Hughes Apr 29 '17 at 19:06
  • @MeirionHughes Where are two or more distinct `Promise` values stored in a single variable at linked Question? The present Question could be a duplicate of an existing Question, though not as to currently linked Question; that is, without one of the answers being adjusted to account for expectation of two or more `Promise` values being available at single `.then()`. – guest271314 Apr 29 '17 at 19:07
  • @MeirionHughes Though since OP apparently is only interested in storing `Promise` value of `weather.getFiveDaySummary()` call, they could define a variable which is set to `Promise` value of `weather.getFiveDaySummary()` returned from `.then()`, omitting returning `weather.getForecastForTime(new Date())` – guest271314 Apr 29 '17 at 19:15
  • fair enough; I'll retract. – Meirion Hughes Apr 29 '17 at 19:36

2 Answers2

0

Assign the Promise returned from yrno.getWeather(LOCATION) call to a variable.

Use Promise.all() to return results from both weather.getFiveDaySummary() and weather.getForecastForTime(new Date()) calls.

Chain .then() to the result of call to get the data at initial and subsequent .then() chained to variable identifier which returned initial Promise values.

let weatherData = yrno.getWeather(LOCATION).then(weather => {
  // note `return`, alternatively omit `return` and `{`, `}` at arrow function
  // .then(weather => Promise.all(/* parameters */))
  return Promise.all([weather.getFiveDaySummary()
                    , weather.getForecastForTime(new Date())]);
});

weatherData
// `results` is array of `Promise` values returned from `.then()`
// chained to `yrno.getWeather(LOCATION).then((weather)`
.then(results => {
  let [fiveDaySummary, forecastForTime] = results; 
  console.log('five day summary:', fiveDaySummary
             , 'current weather:', forecastForTime); 
  // note `return` statement, here
  return results
})
.catch(e => {
  // `throw` `e` here if requirement is to chain rejected `Promise`
  // else, error is handled here
  console.log('an error occurred!', e);
});

// weatherData
// .then(results => { // do stuff with `results` from first `weatherData` call })
// .catch(e => console.log(e));
guest271314
  • 1
  • 15
  • 104
  • 177
  • If you indent your code properly, it would not look so much like you have code after a `return` statement. – jfriend00 Apr 29 '17 at 22:26
0

An alternative to using promises directly is to use await/async.

// weather.js  
const yrno = require('yr.no-forecast')({
  version: '1.9', // this is the default if not provided,
  request: {
    // make calls to locationforecast timeout after 15 seconds
    timeout: 15000
  }
});

const LOCATION = {
  // This is Dublin, Ireland
  lat: 53.3478,
  lon: 6.2597
};

async function getWeather() {

  let weather = await yrno.getWeather(LOCATION);
  let fiveDaySummary = await weather.getFiveDaySummary();
  let forecastForTime = await weather.getForecastForTime(new Date());

  return {
    fiveDaySummary: fiveDaySummary,
    forecastForTime: forecastForTime,
  }
}

async function main() {
  let report;

  try {
    report = await getWeather();
  } catch (e) {
    console.log('an error occurred!', e);
  }

  // do something else... 
  if (report != undefined) {
    console.log(report); // fiveDaySummary and forecastForTime
  }
}

main(); // run it

you can run this (node.js 7) with:

node --harmony-async-await weather

You can use await/async on older targets by using Babel or Typescript to transpile it down for you.

Bonus (based off your comments) - I wouldn't do it this way, but just to show you it can be done:

const http = require('http');

const port = 8080;

http.createServer(
  async function (req, res) {
    let report = await getWeather(); // see above
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.write("" + JSON.stringify(report.fiveDaySummary));
    res.end('Hello World\n');
  })
  .listen(port);

enter image description here

again with node --harmony-async-await weather or transpile it.

Meirion Hughes
  • 24,994
  • 12
  • 71
  • 122