0

I have the following code:

casper.then(function(){

    for (siteID = 1 ; siteID < 10; siteID++) {

        casper.then(function(){

            this.fill('form#form1', {'txtSiteName' : siteID }, true);

        });

        casper.then(function(){

            this.click('#BtnSiteSearch');
            this.wait('500');

        });

        casper.then(function(){

            this.echo("Longitude: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(2) > td:nth-child(2)'));
            this.echo("Latitude: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(3) > td:nth-child(2)'));
            this.echo("City: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(5) > td:nth-child(2)'));
            this.echo("Area: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(7) > td:nth-child(2)'));
            this.echo("Address: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(9) > td:nth-child(2)'));
            this.echo("Access: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(10) > td:nth-child(2)'));
            this.echo("H&S: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(13) > td:nth-child(2)'));
            this.echo("Engineers: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(14) > td:nth-child(2)'));
            this.echo("Owner: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(8) > td:nth-child(2)'));

            this.capture('test' + siteID + '.png');

        });

    }

});

My problem is that the for loop runs and increment to that last number in the for loop. Then the code runs 10x on the last number.

I believe it's something to do with Synchronous and Asynchronous but if I am honest I don't know how to work around the issue.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
ziggy
  • 1
  • Possible duplicate of [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – Artjom B. Dec 25 '15 at 00:30

2 Answers2

1

I am not an expert with casper, but apparently this is a common JavaScript mistake that you often find in every book which explains closures. The anonymous function in the then clause is executed at a later point, the loop is executed straight away, so when the anonymous function is executed, the loop variable is already at its last value, and it is that value which is picked up by your anonymous function.

The common trick recommended against that is to pass the loop variable in a function for immediate evaluation :

for (siteID = 1 ; siteID < 10; siteID++) {

    (function (siteID) {
        casper.then(function(){

            this.fill('form#form1', {'txtSiteName' : siteID }, true);

        })(siteID);
    });

}

In your particular case, I would however check whether the this variable is correctly bound (I guess this is casper is binding it correctly, right?).

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
user3743222
  • 18,345
  • 5
  • 69
  • 75
  • Hi, I appreciate the time you've taken to help me. It has helped me understand what was happening and where I was going wrong. Following your post I have read a few posts on closure and this has helped me too. Many thanks. – ziggy Dec 27 '15 at 02:33
0

I suggest you to use this code:

var casper = require('casper').create({
    verbose: true,
    logLevel: 'debug',
    pageSettings: {
        loadImages: false, // The WebPage instance used by Casper will
        loadPlugins: false, // use these settings
        userAgent: 'Mozilla/5.0 (Macintosh Intel Mac OS X 10_7_5) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4'
    }
});

casper.start();

for (siteID = 1 ; siteID < 10; siteID++) {
    casper.wait(100);
    simpleFunction(siteID)
}

function simpleFunction(siteID){
    casper.then(function(){
        casper.echo(siteID);
    });

    casper.then(function(){

        this.fill('form#form1', {'txtSiteName' : siteID }, true);

    });

    casper.then(function(){

        this.click('#BtnSiteSearch');
        this.wait('500');

    });

    casper.then(function(){

        this.echo("Longitude: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(2) > td:nth-child(2)'));
        this.echo("Latitude: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(3) > td:nth-child(2)'));
        this.echo("City: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(5) > td:nth-child(2)'));
        this.echo("Area: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(7) > td:nth-child(2)'));
        this.echo("Address: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(9) > td:nth-child(2)'));
        this.echo("Access: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(10) > td:nth-child(2)'));
        this.echo("H&S: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(13) > td:nth-child(2)'));
        this.echo("Engineers: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(14) > td:nth-child(2)'));
        this.echo("Owner: " + this.fetchText('#pnlSiteDetail > fieldset > table > tbody > tr:nth-child(8) > td:nth-child(2)'));

        this.capture('test' + siteID + '.png');

    });

}

casper.run();
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • `simpleFunction()` doesn't return anything, so `casper.wait(100,simpleFunction(siteID));` doesn't make sense. This would be functionally equivalent to `simpleFunction(siteID); casper.wait(100);` which is fine. Also, please fix the indentation inside of the for-loop. – Artjom B. Dec 25 '15 at 15:47
  • simpleFunction(siteID) is a function executed for each iteration for-loop so I have used casper.wait() to execute the function – Amine Mohamed Dec 25 '15 at 15:58
  • Well, that reasoning would be correct if `simpleFunction` would be written something like this: `function simpleFunction(siteID){ return function(){ casper.then(function(){...}); ... }}`. `simpleFunction` is executed immediately when inside the loop and not scheduled to be executed later in `casper.wait()`. JavaScript supports passing functions, but if you want that, then you either can't call the function (omitting `()`) or let the function return some anonymous function. – Artjom B. Dec 25 '15 at 17:21
  • Thank you everybody for your assistance and guidance. – ziggy Dec 27 '15 at 02:34