Что значит "тогда" в CasperJS

Я использую CasperJS для автоматизации серии кликов, заполненных форм, анализа данных и т.д. через веб-сайт.

Кажется, Каспер организован в список предустановленных шагов в форме операторов then (см. их пример здесь: http://casperjs.org/quickstart.html), но неясно, что запускает следующий оператор.

Например, then ожидает завершения всех ожидающих запросов? Рассчитывает ли injectJS как ожидающий запрос? Что произойдет, если у меня есть инструкция then, вложенная в цепочку в конце инструкции 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
});

Я ищу техническое объяснение того, как поток работает в CasperJS. Моя конкретная проблема заключается в том, что мой последний оператор then (выше) работает до моего оператора casper.open, и я не знаю почему.

Ответ 1

then() в основном добавляет новый шаг навигации в стек. Шаг - это функция javascript, которая может выполнять две разные вещи:

  • ожидание предыдущего шага - если выполнено -
  • ожидание запроса URL-адреса и связанной страницы для загрузки

Возьмем простой сценарий навигации:

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)');
});

Вы можете распечатать все созданные шаги в стеке следующим образом:

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

Это дает:

$ 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)'); }"
]

Обратите внимание на функцию _step(), которая автоматически добавлена ​​CasperJS для загрузки URL-адреса для нас; при загрузке URL-адреса вызывается следующий шаг, доступный в стеке - step3() -.

Когда вы определили свои шаги навигации, run() выполняет их поочередно:

casper.run();

Сноска: материал обратного вызова/прослушивателя представляет собой реализацию шаблона Promise.

Ответ 2

then() просто регистрирует последовательность шагов.

run() и его семейство функций бегуна, обратных вызовов и слушателей - все, что на самом деле выполняет работу по выполнению каждого шага.

Всякий раз, когда шаг завершен, CasperJS будет проверять три флага: pendingWait, loadInProgress и navigationRequested. Если какой-либо из этих флагов истинен, то ничего не делайте, простаивайте до более позднего времени (setInterval style). Если ни один из этих флагов не является истинным, следующий шаг будет выполнен.

Как и в случае с CasperJS 1.0.0-RC4, существует недостаток, когда в определенных временных условиях метод "попытаться сделать следующий шаг" будет срабатывать до того, как у CasperJS было время поднять либо один из loadInProgress или navigationRequested. Решение состоит в том, чтобы поднять один из этих флагов, прежде чем покинуть любой шаг, на котором ожидается, что эти флаги будут подняты (например: поднять флаг до или после запроса casper.click()), возможно, так:

(Примечание: это только иллюстративное, более похожее на psuedocode, чем на правильную форму 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()
}

Чтобы обернуть это решение в одну строку кода, я ввел blockStep() в этот github запрос pull, расширяя click() и clickLabel() как средство, чтобы гарантировать, что мы получим ожидаемое поведение при использовании then(). Проверьте запрос на получение дополнительной информации, шаблонов использования и минимальных тестовых файлов.