Question

I am new in JavaScript programming and I have got no idea even how to ask Google, so i will describe in detail what I would like to achieve.

I am using node.js and xml2js module to create DataProvider class for jasmine, that parse simple XML file:

<Tests>
    <Test name="test1">
        <values>2,4,6</values>
        <expectations>1,2,3</expectations>
    </Test>
    <Test name="test2">
        <values>8,10,12</values>
        <expectations>4,5,6</expectations>
    </Test>
</Tests>

I would like DataProvider constructor to parse XML immediately and have result visible in this.testData. The script is:

var fs = require('fs'),
    xml2js = require('xml2js'),
    util = require('util');

var DataProvider = function(dataPath) {
    this.dataPath = dataPath;
    this.testData;

    fs.readFile(this.dataPath, function(err, data) {
        var parser = new xml2js.Parser();
        parser.parseString(data, function(err, result) {
            //console.log(util.inspect(result, false, null));
            this.testData = result;
        });
    });
};

I am using it like follows:

var dp = new DataProvider("C:\\data2.xml");
console.log(dp.testData);

When I am running the script >node script.js I am receiving undefined. When I uncomment console.log(util.inspect(result, false, null)); I am receiving

undefined
{ Tests: 
   { Test: 
      [ { '$': { name: 'test1' },
          values: [ '2,4,6' ],
          expectations: [ '1,2,3' ] },
        { '$': { name: 'test2' },
          values: [ '8,10,12' ],
          expectations: [ '4,5,6' ] } ] } }

so generally it works, but how to assign result to this.testData?

Is this connected with scope of this.testData?

Was it helpful?

Solution

JavaScript utilises async paradigm a lot, so when you read file for example - function you provide as callback will be executed in own scope. this is related to scope, so you will lose it. After you are trying to reference this.testData will actually not reference DataProvider object, but scope of anonymous function you've provided to parseString.

So there are few ways to overcome, and easiest here would be persist this in variable in upper scope, so you can reference it, call it for example self, some devs prefer that.

var DataProvider = function(dataPath) {
    this.dataPath = dataPath;
    this.testData;

    var self = this;

    fs.readFile(this.dataPath, function(err, data) {
        var parser = new xml2js.Parser();
        parser.parseString(data, function(err, result) {
            //console.log(util.inspect(result, false, null));
            self.testData = result;
        });
    });
};

Or by using .bind, but dissadvantage here is that it has to be used twice (as you have two scopes to 'travel' through:

var DataProvider = function(dataPath) {
    this.dataPath = dataPath;
    this.testData;

    fs.readFile(this.dataPath, function(err, data) {
        var parser = new xml2js.Parser();
        parser.parseString(data, function(err, result) {
            //console.log(util.inspect(result, false, null));
            this.testData = result;
        }.bind(this));
    }.bind(this));
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top