Подтвердить что ты не робот

Что должно быть завернуто в() заявлениях в CasperJS? Как определить порядок выполнения функций sync/async?

Мне сложно определить, что такое асинхронное и что не работает при запуске CasperJS, что должно быть завершено в операторы then() и что будет оцениваться, когда.

Я столкнулся с проблемой где-то, что связано с инструкцией об отказе от переполнения, переменной scope или выражением()(), и я начну обматывать весь мой код в then() заявлениях... которая, оказывается, не является проблемой.

Я замечаю, что мой код работает на двух уровнях, когда я проходил через него, уровень оценки, который анализирует код, а затем приходят операторы then(). Кроме того, мои заявления печати появляются в иногда необъяснимом порядке.

Мой вопрос: как эти операторы then() фактически попадают в очередь? Я читал документы, и я понимаю. Я хочу понять правила и вырезать и высушить пути, чтобы определить, что такое синхронизация и что такое асинхронный процесс.

Я даже читал части книги по асинхронному кодированию, но на самом деле ничего не похоже на структуру CasperJS. Любые ресурсы?

Кроме того, какая наилучшая практика для того, где поставить ваши операторы then()? Должны ли они быть покрыты либерально повсюду или они должны находиться в контрольной основной функции casper.begin(), которая вызывает других?

Спасибо, ребята, я привык к PHP.

4b9b3361

Ответ 1

Правило большого значения: Все функции CasperJS, содержащие слова then и wait, являются асинхронными. У этого оператора есть много исключений.

Что работает then()?

CasperJS организован как последовательность шагов, которые обрабатывают поток управления вашего script. then() обрабатывает многие типы событий PhantomJS/SlimerJS, которые определяют окончание шага. Когда вызывается then(), переданная функция помещается в очередь очереди, которая является просто массивом JavaScript. Если предыдущий шаг завершился, либо потому, что он был простой синхронной функцией, либо потому, что CasperJS обнаружил, что определенные события, где запускается, следующий шаг будет запущен и повторить это, пока не будут выполнены все этапы.

Все эти функции шага привязаны к объекту casper, поэтому вы можете ссылаться на этот объект, используя this.

Следующий простой script показывает два шага:

casper.start("http://example.com", function(){
    this.echo(this.getTitle());
}).run();

Первым шагом является неявный асинхронный ( "ступенчатый" ) open() вызов за start(). Функция start() также принимает необязательный обратный вызов, который сам является вторым шагом в этом script.

Во время выполнения первого шага открывается страница. Когда страница полностью загружена, PhantomJS запускает событие onLoadFinished, CasperJS запускает свой собственный events и продолжает следующий шаг. Второй шаг - простая полностью синхронная функция, поэтому здесь ничего необычного не происходит. Когда это будет сделано, CasperJS выйдет, потому что больше нет шагов для выполнения.

Существует исключение из этого правила: когда функция передается в функцию run(), она будет выполняться как последний шаг, а не по умолчанию. Если вы не вызываете exit() или die() там, вам нужно будет убить процесс.

Как then() обнаруживает, что следующий шаг должен ждать?

Возьмем, например, следующий пример:

casper.then(function(){
    this.echo(this.getTitle());
    this.fill(...)
    this.click("#search");
}).then(function(){
    this.echo(this.getTitle());
});

Если во время выполнения шага запускается событие, означающее загрузку новой страницы, CasperJS будет ждать загрузки страницы до выполнения следующего шага. В этом случае был вызван щелчок, который сам вызвал onNavigationRequested событие из основного браузера. CasperJS видит это и приостанавливает выполнение с использованием обратных вызовов, пока не загрузится следующая страница. Другие типы таких триггеров могут быть представлены в форме или даже когда клиентский JavaScript делает что-то вроде своей собственной переадресации с помощью window.open()/window.location.

Конечно, это ломается, когда мы говорим о приложениях с одной страницей (со статическим URL). PhantomJS не может обнаружить, что, например, после клика визуализируется другой шаблон, поэтому он не может дождаться завершения загрузки (это может занять некоторое время, когда данные загружаются с сервера). Если следующие шаги зависят от новой страницы, вам нужно будет использовать, например. waitUntilVisible() для поиска селектора, который уникален для загружаемой страницы.

Как вы называете этот стиль API?

Некоторые люди называют это Promises, из-за того, что шаги могут быть связаны цепью. Помимо имени (then()) и цепочки действий, это конец сходства. Нет результата, который передается от обратного вызова к обратному вызову через шаговую цепь в CasperJS. Либо вы сохраняете результат в глобальной переменной, либо добавляете ее в объект casper. Тогда есть только ограниченная обработка ошибок. Когда встречается ошибка, CasperJS будет умирать в конфигурации по умолчанию.

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

Конечно, это не вся правда, называя его шаблоном строителя. Шаги могут быть вложенными, что на самом деле означает, что если вы планируете шаг внутри другого шага, он будет помещен в очередь после текущего шага и после всех остальных шагов, которые уже запланированы с текущего шага. (Это много шагов!)

Следующий script является хорошей иллюстрацией того, что я имею в виду:

casper.on("load.finished", function(){
    this.echo("1 -> 3");
});
casper.on("load.started", function(){
    this.echo("2 -> 2");
});
casper.start('http://example.com/');
casper.echo("3 -> 1");
casper.then(function() {
    this.echo("4 -> 4");
    this.then(function() {
        this.echo("5 -> 6");
        this.then(function() {
            this.echo("6 -> 8");
        });
        this.echo("7 -> 7");
    });
    this.echo("8 -> 5");
});
casper.then(function() {
    this.echo("9 -> 9");
});
casper.run();

Первое число показывает положение синхронного фрагмента кода в script, а второе показывает фактическое выполненное/напечатанное положение, поскольку echo() является синхронным.

Важные моменты:

  • Сначала номер 3
  • Число 8 печатается между 4 и 5

Чтобы избежать путаницы и трудно найти проблемы, всегда вызывайте асинхронные функции после синхронных функций за один шаг. Если это кажется невозможным, разделите на несколько шагов или рассмотрите рекурсию.

Как работает waitFor()?

waitFor() - самая гибкая функция в семействе wait*, потому что каждая другая функция использует этот.

waitFor() планирует в своей основной форме (передавая только одну функцию проверки и ничего больше) на один шаг. Функция check, которая передается в нее, вызывается повторно до тех пор, пока не будет выполнено условие или не будет достигнут (глобальный) тайм-аут. Когда дополнительная функция then и/или onTimeout передается дополнительно, она будет вызываться в этих случаях.

Важно отметить, что если waitFor() истечет время ожидания, script прекратит выполнение, если вы не передали функцию обратного вызова onTimeout, которая по сути является функцией catch catch:

casper.start().waitFor(function checkCb(){
    return false;
}, function thenCb(){
    this.echo("inner then");
}, null, 1000).then(function() {
    this.echo("outer");
}).run();

Что представляют собой другие функции, которые также являются асинхронными функциями шага?

Начиная с 1.1-beta3, существуют следующие дополнительные асинхронные функции, которые не следуют правилу большого пальца:

Модуль Каспер: back(), forward(), reload(), repeat(), start(), withFrame(), withPopup()
Модуль тестера: begin()

Если вы не уверены в исходном коде , использует ли конкретная функция then() или wait().

Являются ли асинхронные прослушиватели событий?

Слушатели событий могут быть зарегистрированы с помощью casper.on(listenerName, callback), и они будут запущены с помощью casper.emit(listenerName, values). Что касается внутренних компонентов CasperJS, они не являются асинхронными. Асинхронная обработка происходит от функций, в которых эти вызовы emit() лежат. CasperJS передает большинство событий PhantomJS просто, так что здесь они асинхронны.

Могу ли я вырваться из потока управления?

Поток управления или исполнения - это способ, которым CasperJS выполняет script. Когда мы выходим из потока управления, нам нужно управлять вторым потоком (или даже больше). Это значительно усложнит разработку и поддержку script.

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

function longRunningFunction(callback) {
    ...
    callback(data);
    ...
}
var result;
casper.start(url, function(){
    longRunningFunction(function(data){
        result = data;
    });
}).then(function(){
    this.open(urlDependsOnFunResult???);
}).then(function(){
    // do something with the dynamically opened page
}).run();

Теперь мы имеем два потока, которые зависят друг от друга.

Другие способы прямого разделения потока - использование функций JavaScript setTimeout() и setInterval(). Поскольку CasperJS предоставляет waitFor(), нет необходимости использовать их.

Можно ли вернуться к потоку управления CasperJS?

Когда поток управления должен быть объединен обратно в поток CasperJS, существует очевидное решение, устанавливая глобальную переменную и одновременно ожидая ее установки.

Пример такой же, как в предыдущем вопросе:

var result;
casper.start(url, function(){
    longRunningFunction(function(data){
        result = data;
    });
}).waitFor(function check(){
    return result; // `undefined` is evaluated to `false`
}, function then(){
    this.open(result.url);
}, null, 20000).then(function(){
    // do something with the dynamically opened page
}).run();

Что такое асинхронность в тестовой среде (модуль тестера)?

Технически ничто не является асинхронным в модуле тестера. Вызов test.begin() просто выполняет обратный вызов. Только когда сам обратный вызов использует асинхронный код (значение test.done() вызывается асинхронно внутри одного обратного вызова begin()), другие тестовые примеры begin() могут быть добавлены в очередь тестового случая.

Вот почему один тестовый пример обычно состоит из полной навигации с casper.start() и casper.run(), а не наоборот:

casper.test.begin("description", function(test){
    casper.start("http://example.com").run(function(){
        test.assert(this.exists("a"), "At least one link exists");
        test.done();
    });
});

Лучше всего придерживаться полного потока внутри begin(), так как вызовы start() и run() не будут смешиваться между несколькими потоками. Это позволяет использовать несколько полных тестовых примеров для каждого файла.


Примечания:

  • Когда я говорю о синхронных функциях/выполнении, я имею в виду блокирующий вызов, который может фактически вернуть то, что он вычисляет.