0

Looks nobody on internet describes similar problem, and similar solution is not working for me. I am trying to scrape webpage so I created class for parser and one of the method looks like follows:

 get chListUrl() {
    return "http://www.teleman.pl/program-tv/stacje";
  }

getChannels() {
     var dict = {};
     axios.get(this.chListUrl).then(function (response) {  
       var $ = cheerio.load(response.data);       
        var ile_jest_stacji = $('#stations-index a').length;
        $('#stations-index a').each( (i,elem) => {
          let href = $(elem).attr('href');
          let kodstacji = href.replace(/\/program-tv\/stacje\//ig,'');     
          let nazwastacji = $(elem).text();
          dict[nazwastacji]=kodstacji;
        });
        return  dict;
        
    }).catch(function (error) {
        console.log(error);
        return null;
    }).finally(function() {
        console.log("Koniec");
        
    });
  }

And problem is getChannels must be indirectly asynchronous because it contains axios BUT

let tm = new TM();
var a = tm.getChannels();

a is always undefined and it should be dictionary! Such construct means "assing to variable a result of execution of tm.getChannels()" so assignment should always be done AFTER whole function ends. Otherwise such syntax in language is useless because you will never be sure what value is stored in variable, and such errors are difficult to find and debug.

var a = await tm.getChannels();

NOT WORKING -> SyntaxError: await is only valid in async function (huh?)

adding async to getChannels() changes nothing.
Assing async to getChannels() and remove 'await' from assignment returns Promise{undefined} (huh?)
putting async before axios changes nothing as response is already handled by .then()
changing return dict to return await dict gives another "await is only valid in async function" (huh? axios is asynchronous)

I'm scratching my head over this for 2 weeks. In Swift when something is return in completion handler it is assigned to variable in proper moment, why something returned by Promise not works the same way?

Sebastian
  • 448
  • 4
  • 14
  • Does this answer your question? [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Klaycon May 11 '20 at 20:54
  • `Such construct means "assing to variable a result of execution of tm.getChannels()" so assignment should always be done AFTER whole function ends.` - It **IS** done after the whole function ends. Your `getChannels()` function does not immediately execute the function you pass to `.then(...)`. You give this function as a *callback* to a *Promise* - you tell the interpreter, "run this code when the request is done" and the interpreter saves the code **and then returns without running it yet**. – Klaycon May 11 '20 at 20:56
  • Axios is internal function in GetChannels. GetChannels does not return any promises it should return dictionary, so if I understand correctly Promises are useless in JavaScript if they cannot return a value:( , what a sad story – Sebastian May 15 '20 at 22:29
  • They can return a value. They can return a value asynchronously, but not synchronously. This is very far from useless. It means you can continue executing code while waiting for your HTTP request to finish. It means you can start multiple HTTP requests at the same time. You simply need to adjust your code around this new pattern - `getChannels()` relies on an HTTP request, so it **cannot** return a dictionary directly, you must give it a callback or have it return a promise. That's asynchronous javascript for you. – Klaycon May 18 '20 at 20:17

2 Answers2

1

You need to be inside an async function to use the await expression:

The await operator is used to wait for a Promise. It can only be used inside an async function.

Example sourced from MDN:

async function f1() {
  var x = await resolveAfter2Seconds(10);
  console.log(x); // 10
}

f1();

Fixing your issue

class TM {

  get chListUrl() {
    return "http://www.teleman.pl/program-tv/stacje";
  }

  async getChannels() { // you need not use get syntax 
    let dict = {};
    try { // we will be handling possible errors with try catch instead of reject
        const response = await axios.get(this.chListUrl);
        let $ = cheerio.load(response.data);       
        let ile_jest_stacji = $('#stations-index a').length;
        $('#stations-index a').each( (i,elem) => {
          let href = $(elem).attr('href');
          let kodstacji = href.replace(/\/program-tv\/stacje\//ig,'');     
          let nazwastacji = $(elem).text();
          dict[nazwastacji]=kodstacji;
        });
      return dict;
     } catch(ex) {
      console.log(ex);
      return null
     }
  }

}

// let's make it work!
(async function() {
  const tm = new TM()
  const channels = await tm.getChannels()
  // do whatever you want with channels
  console.log(channels)
})()

Now, you're probably not going to call getChannels out of nowhere like this instead you will probably be inside a function that you yourself defined, you need to add the async keyword to this function. Whatever block function your code is in needs to be async.

Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • 1
    Thanks i am going to try but I always struggle to understand what is going on with such weird syntax (function)() which brings me another question - why Promises exists if you cannot return value from them – Sebastian May 15 '20 at 22:18
  • Thank you for taking time to help :) I used then because I found this on some blog and author didn’t dare to explain about nuances with returning value,now it should be easier :) – Sebastian May 15 '20 at 23:05
  • The problem is not wrong await but actually that you expected ‘return dict’ to return it all the way where in fact its returning on the then clause – Ohad Bitton May 24 '22 at 08:07
0

If you want to use the async/await syntax you should remove the .then() syntax and you can resolve that way:

async getChannels() {
     const response = await axios.get(this.chListUrl);
     return response
  }

You can learn more about async/await in the link: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

  • I don’t what to use a sync await. My problem is I have function with Promise that apparently can not properly return value and assign it to variable, that’s the deal – Sebastian May 15 '20 at 22:19