0

I am writing a class in JavaScript that would send an HTTP request for a specified URL and then would return the body of the response. I am new to Node.js and JavaScript, therefore, I am having a really hard time understanding the callbacks and async nature of Nodejs.

I wrote the actual method that fetches the URL, and it works fine. The result is available passed to the Mocha test.

class HttpObject {
  constructor () {
    this.url = null
    this.userAgent = null
    this.body = null
  }

  fetchUrl (url, userAgent, callback) {
    this.url = url
    this.userAgent = userAgent

    const request = require('request')
    request(this.url, { timeout: 10000 }, function (error, response, body) {
      if (!error && response.statusCode === 200) {

        //the next line doesn't work, the 'body' field in Mocha test is null
        this.body = response.body
        return callback(response, false)
      } else {
        return callback(null, error)
      }
    })
  }
}

But when I test the body field of the HttpObject, it's still assigned null. Though, it should have been assigned the body of the result.

it('should send and http request to https://www.google.com', function (done) {
  httpObj.fetchUrl('https://www.google.com', 'Mozilla/5.0', (res, err) => {
    assert.strictEqual(httpObj.getUrl(), 'https://www.google.com')
    assert.strictEqual(httpObj.getPort(), 80)
    assert.strictEqual(httpObj.getUserAgent(), 'Mozilla/5.0')

    // previous tests pass, but the following doesn't
    assert.notStrictEqual(httpObj.getBody(), null)

    done()
  })
})
Niklas Higi
  • 2,188
  • 1
  • 14
  • 30

2 Answers2

1

The issue is that the context this is lost when a function is called from outside the class that created that function. ( mostly a callback )

In your case, because function (error, response, body) is being called from somewhere else, it doesn't know the value of this.

To solve this, either you can use fat arrow functions like this:

request(this.url, { timeout: 10000 }, (error, response, body) => {
    // you can now set this.body here safely
})

or you can use .bind(this) on the callback

request(this.url, { timeout: 10000 }, function (error, response, body) {
    // you can now set this.body here safely
}.bind(this))
Anand Undavia
  • 3,493
  • 5
  • 19
  • 33
0

Your this.body statement in the your HttpObject is in a different scope then the rest of your this values because its a callback of request. Try adding a var that = this above the callback from your request.

class HttpObject {
  constructor () {
    this.url = null
    this.userAgent = null
    this.body = null
  }

  fetchUrl (url, userAgent, callback) {
    // add var that
    var that = this;
    this.url = url
    this.userAgent = userAgent

    const request = require('request')
    request(this.url, { timeout: 10000 }, function (error, response, body) {
      if (!error && response.statusCode === 200) {

        // Change the value to that.
        that.body = response.body

        return callback(response, false)
      } else {
        return callback(null, error)
      }
    })
  }
}
Trevor Reimer
  • 304
  • 2
  • 14