web-dev-qa-db-fra.com

Que signifie-t-il vraiment dans CasperJS?

J'utilise CasperJS pour automatiser une série de clics, de formulaires complétés, d'analyser des données, etc. via un site Web.

Casper semble être organisé en une liste d'étapes prédéfinies sous la forme d'instructions then (voir leur exemple ici: http://casperjs.org/quickstart.html ) mais on ignore ce qui déclenche l'exécution de l'instruction suivante .

Par exemple, then attend-il que toutes les demandes en attente soient terminées? injectJS compte-t-il comme demande en attente? Que se passe-t-il si une instruction then est imbriquée - enchaînée à la fin d'une instruction open?

casper.thenOpen('http://example.com/list', function(){
    casper.page.injectJs('/libs/jquery.js');
    casper.evaluate(function(){
        var id = jQuery("span:contains('"+itemName+"')").closest("tr").find("input:first").val();
        casper.open("http://example.com/show/"+id); //what if 'then' was added here?
    });
});

casper.then(function(){
    //parse the 'show' page
});

Je cherche une explication technique sur le fonctionnement du flux dans CasperJS. Mon problème spécifique est que ma dernière instruction then (ci-dessus) s'exécute avant mon instruction casper.open et je ne sais pas pourquoi.

97
bendytree

then() ajoute fondamentalement une nouvelle étape de navigation dans une pile. Une étape est une fonction javascript qui peut faire deux choses différentes:

  1. en attente de l'étape précédente - le cas échéant - en cours d'exécution
  2. en attente d'une URL demandée et d'une page associée à charger

Prenons un scénario de navigation simple:

var casper = require('casper').create();

casper.start();

casper.then(function step1() {
    this.echo('this is step one');
});

casper.then(function step2() {
    this.echo('this is step two');
});

casper.thenOpen('http://google.com/', function step3() {
    this.echo('this is step 3 (google.com is loaded)');
});

Vous pouvez imprimer toutes les étapes créées dans la pile de la manière suivante:

require('utils').dump(casper.steps.map(function(step) {
    return step.toString();
}));

Ça donne:

$ casperjs test-steps.js
[
    "function step1() { this.echo('this is step one'); }",
    "function step2() { this.echo('this is step two'); }",
    "function _step() { this.open(location, settings); }",
    "function step3() { this.echo('this is step 3 (google.com is loaded)'); }"
]

Notez la fonction _step() qui a été ajoutée automatiquement par CasperJS pour charger l’URL pour nous; Lorsque l'URL est chargée, l'étape suivante disponible dans la pile, à savoir step3(), est appelée.

Lorsque vous avez défini vos étapes de navigation, run() les exécute une par une de manière séquentielle:

casper.run();

Note de bas de page: le programme callback/listener est une implémentation du modèle Promise .

93
NiKo

then() enregistre simplement une série d'étapes.

run() et sa famille de fonctions de coureur, de callbacks et d’écouteurs sont tous ce qui fait le travail d’exécution de chaque étape.

Chaque fois qu'une étape est terminée, CasperJS vérifie trois indicateurs: pendingWait, loadInProgress et navigationRequested. Si l'un de ces indicateurs est vrai, ne faites rien, restez inactif jusqu'à une date ultérieure (style setInterval). Si aucun de ces indicateurs n'est vrai, l'étape suivante sera exécutée.

Depuis CasperJS 1.0.0-RC4, il existe une faille dans laquelle, dans certaines circonstances temporelles, la méthode "essayez de faire la prochaine étape" sera déclenchée avant que CasperJS ait eu le temps de lever l'un des drapeaux loadInProgress ou navigationRequested. La solution consiste à lever l'un de ces drapeaux avant de quitter une étape où ces drapeaux sont censés être levés (ex: lever un drapeau soit avant, soit après avoir demandé une casper.click()), peut-être comme suit:

(Note: Ceci est seulement illustratif, ressemble plus à psuedocode qu'à la forme correcte de CasperJS ...)

step_one = function(){
    casper.click(/* something */);
    do_whatever_you_want()
    casper.click(/* something else */); // Click something else, why not?
    more_magic_that_you_like()
    here_be_dragons()
    // Raise a flag before exiting this "step"
    profit()
}

Pour résumer cette solution dans une seule ligne de code, j'ai introduit blockStep() dans ce github requête d'extraction , en étendant click() et clickLabel() afin de garantir que le comportement attendu par then() est obtenu. Consultez la demande pour plus d'informations, les modèles d'utilisation et les fichiers de test minimum.

33
starlocke