2

I have copied this example in CasperJS.
How would I implement a 'continue' command in combination?

casper.then(function() {
    var current = 1;
    var end = 4;
    var something = true;

    for (;current < end;) {

      (function(cntr) {
        casper.thenOpen('about:blank', function() {
              this.echo('loop number: '+ cntr);

        });
        casper.then(function(){
            if (something) {
                continue; // ERROR: 'continue' is only valid inside a loop statement
            }
        });
        casper.then(function(){
            this.echo('this still gets printed');
        });
      })(current);
      current++;
    }
});

I'm getting a 'only valid inside a loop' error but... it is inside a loop?

Community
  • 1
  • 1
ProGirlXOXO
  • 2,170
  • 6
  • 25
  • 47

4 Answers4

2

All casper.then* and casper.wait* functions are asynchronous. The step functions that you pass into them are in fact executed after the loop has finished. Therefore you cannot use continue to jump to the next iteration.

You can however nest all step functions, which achieves what you expect:

casper.then(function() {
    var current = 1;
    var end = 4;
    var something = true;

    while(current < end) {

        (function(cntr) {
            casper.thenOpen('about:blank', function() {
                this.echo('loop number: '+ cntr);
            });
            casper.then(function(){
                if (something) {
                    return; // this replaced `continue`
                }
                // more synchronous code
                this.then(function(){
                    this.echo('this still gets printed');
                });
            });
        })(current);
        current++;
    }
});

It's a bit shorter at the expense of one additional indentation level.

Artjom B.
  • 61,146
  • 24
  • 125
  • 222
  • So I would have to indent an extra level for every potential return point. As fan of Python I'm not a fan of that method. – ProGirlXOXO Mar 16 '16 at 00:39
1

It is not inside the loop. When you isolate your scope with an anonymous function you lose the "loop scope" reference, so your continue does not know where the loop is. Although you can use closures (variables outside of the functions scope), you can't refer an outside loop...

See in the console:

for (var i=0;i<10;i++) continue;

works like a charm, when

for (var i=0;i<10;i++) (function(){ continue; })();

does not work because the loop reference is lost.

How to deal with that? Just return a result from your function, e.g.

for (var i=0;i<10;i++) {
  if ((function(){ return i === 3; })()) {
    console.log('continued'); 
    continue;
  }
}
smnbbrv
  • 23,502
  • 9
  • 78
  • 109
1

Once you are inside an inner function (you actually have two levels, the IIFE around the body of the loop and the function you're passing to thenOpen), you're no longer in the scope of the loop.

Also, the functions that are passed to casper.then and casper.thenOpen execute asynchronously (but in order). If you want to affect the order, you have to check values from within those functions.

casper.then(function() {
    var current = 1;
    var end = 4;
    var something = true;
    var shouldContinue = false;

    for (;current < end;) {

      (function(cntr) {
        casper.thenOpen('about:blank', function() {
          shouldContinue = false;
          this.echo('loop number: '+ cntr);    
        });
      })(current, s);

      (function(cntr) {
        casper.then(function(){
          if (something) {
            shouldContinue = true;
          }
        });
      })(current);


      (function(cntr) {
        if (shouldContinue) {
            return;
        }
        casper.then(function(){
          this.echo('this still gets printed');
        });
      })(current);

      current++;
    }
});
Ruan Mendes
  • 90,375
  • 31
  • 153
  • 217
  • This throws the same error. CasperJS is a strange beast. – ProGirlXOXO Mar 15 '16 at 17:37
  • @ProGirlXOXO Sorry, I wasn't very careful with my answer, fixed it. – Ruan Mendes Mar 15 '16 at 17:48
  • By the way, I would post this at programmer's stack exchange and ask for an improvement suggestion, this code looks pretty gnarly. – Ruan Mendes Mar 15 '16 at 17:53
  • Does not work as intended. OUTPUT: loop number: 1 this still gets printed loop number: 2 this still gets printed loop number: 3 this still gets printed Done. – ProGirlXOXO Mar 15 '16 at 17:54
  • I see, the call to `casper.then` is asynchronous, which means that `shouldContinue` won't be set until later... Unfortunately I don't have time to dig further, but if you add logging to `if(shouldContinue)`, you'll see that that is executing before you set `shouldContinue` to true – Ruan Mendes Mar 15 '16 at 17:58
  • @ProGirlXOXO I think I have something that works, you can't get the `continue` keyword to work because that only works with synchronous code. I've reworked your code so it skips the last step like a continue, pretty ugly hack though – Ruan Mendes Mar 15 '16 at 18:08
  • How do you 'add logging' to if(shouldContinue) this seems like a skill I should have... – ProGirlXOXO Mar 15 '16 at 18:09
  • 1
    You already are logging stuff: `this.echo('loop number: '+ cntr);` – Ruan Mendes Mar 15 '16 at 18:10
  • maybe I missed something but this does not output regardless of something and shouldContinue values so I'm suspicious casper is just crashing. – ProGirlXOXO Mar 15 '16 at 19:02
1

This solution works but I'm not very happy about it. Added an if/else to every casper.then section with a global skip variable. As soon as a continue condition is met all following casper.thens are effectively skipped. Skip is reset at the start of each loop.

casper.start();
    casper.then(function() {
        var current = 1;
        var end = 4;
        var skip;

    for (;current < end;) {
      (function(cntr) {
        casper.thenOpen('about:blank', function() {
              this.echo('loop number: '+ cntr);
              skip = false;
        });
        casper.then(function(){
            if (skip) {
                this.echo('skipping');
            }
            else {
                // put all code here that was in casper.then
                this.echo('proceed as normal');
                skip = true;

            }
        });

        casper.then(function(){
            if (skip) {
                this.echo('skipping');
            }
            else {
                // put all code here that was in casper.then
                this.echo('this no longer gets printed');
            }
        });
      })(current);
      current++;
    }
});
ProGirlXOXO
  • 2,170
  • 6
  • 25
  • 47
  • I know, it's not pretty, but it's the nature of async code, you can't expect synchronous code to run at the same time as asynchronous code. casper at least makes sure your asynchronous code runs in order, which means all code has to be within the functions passed to `then`, and therefore that code cannot use tell a loop in the outer function to `continue` – Ruan Mendes Mar 17 '16 at 13:57