Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ES6 async/await in classes

I'm trying to create a class that will send a post request (login), save the cookie and use that cookie for other operations such as download a file.

I created a local server that that will receive a post http method with user and password in it and a router called /download that will only be accessed if the user is logged in, otherwise it will return you need to log in.

The problem: This is the prototype of my class (before hand):

const request = require('request-promise-native')

class ImageDownloader {
  constructor(username = null, password = null) {
    this.username = username
    this.password = password
    this.cookie = request.jar()

    this.init()
  }

  init() {
    // login and get the cookie
  }

  download() {
    // needs the cookie
  }

  query() {
    // needs the cookie
  }
}

As you can see in the code above I need the cookie for two operations that is download and query so I though about creating an init method that will do the initial operations such as login and call it right in the constructor so it will be initialized and put the cookie on the variable this.cookie to use everywhere, but it doesn't work, it seems that init is being called after every other method.

const request = require('request-promise-native')

class ImageDownloader {
  constructor(username = null, password = null) {
    this.username = username
    this.password = password
    this.cookie = request.jar()

    this.init()
  }

  async init() {
    await request({
      uri: 'http://localhost/login',
      jar: this.cookie,
      method: 'post',
      formData: {
        'username': 'admin',
        'password': 'admin'
      }
    }).catch(e => console.error(e))
  }

  async download() {
    await request({
      uri: 'http://localhost/download/image.jpg',
      jar: this.cookie
    })
    .then(b => console.log(b))
    .catch(e => console.error(e))
  }

  query() {
    // ...
  }
}

const downloader = new ImageDownloader
downloader.download()

It's returning to me that I need to log in (server response)... BUT it works if I do this change:

async download() {
  await init() // <<<<<<<<<<<<
  await request({
    uri: 'http://localhost/download/image.jpg',
    jar: this.cookie
  })
  .then(b => console.log(b))
  .catch(e => console.error(e))
}

It only works if I call init in the download method.

If I put console.log(this.cookie) in download it returns an empty CookieJar and if I put the same in init it will return the right cookie but it appears AFTER the execution of download even tho I called it on the constructor before calling download.

How to solve that? Thank you very much.

@edit

I made the changes that @agm1984 and @Jaromanda X told me but it still doesn't work :(

const request = require('request-promise-native')

class ImageDownloader {
  constructor(username = null, password = null) {
    this.username = username
    this.password = password
    this.cookie = request.jar()

    this.init().catch(e => console.error(e))
  }

  async init() {
    return await request({
      uri: 'http://localhost/login',
      jar: this.cookie,
      method: 'post',
      formData: {
        'username': 'admin',
        'password': 'admin'
      }
    })
  }

  async download() {
    return await request({
      uri: 'http://localhost/download/image.jpg',
      jar: this.cookie
    })
  }

  query() {
    // ...
  }
}

const downloader = new ImageDownloader
downloader.download()
          .then(b => console.log(b))
          .catch(e => console.error(e))

But then again... it doesn't work unless I call init inside download.

like image 448
vajehu Avatar asked Oct 07 '17 23:10

vajehu


1 Answers

The problem here is that init is asynchronous. Using it like this:

const downloader = new ImageDownloader;
downloader.download();

The download function is being executed while init still did not finish yet.

I wouldn't call the init method in the constructor. What I would do is something like this:

1- remove the init call from the constructor.
2- use the class like this:

const downloader = new ImageDownloader();
downloader.init()
  .then(() => downloader.download());

and if you are calling init in an async function you can do:

await downloader.init();
const result = await downloader.download();
like image 122
Omar Avatar answered Sep 24 '22 07:09

Omar