“然后”在CasperJS中真正意味着什么


97

我正在使用CasperJS通过网站自动执行一系列单击,完成的表单,解析数据等操作。

Casper似乎以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
我仍在寻找flowcasperjs 的一般性解释,但是我发现您基本上无法在evaluate调用中引用casper 。(即,您无法打开新的URL,日志,回显等)。因此,在我的情况下,评估被调用,但无法与外界互动。
bentytree 2012年

1
我想知道完全一样的东西,但是懒得问。好问题!
内森

4
evaluate()用于在“浏览器”中运行的代码,phantomjs在页面的DOM中进行浏览。因此没有casper.open,但是可能有jQuery。因此,您的示例没有任何意义,但我仍然想知道then()实际上是什么。
内森

Answers:


93

then()基本上在堆栈中添加了新的导航步骤。步骤是一个javascript函数,可以执行两种不同的操作:

  1. 等待上一步(如果有)正在执行
  2. 等待请求的网址和相关页面加载

让我们来看一个简单的导航方案:

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后,将调用堆栈中的下一步-即step3()

定义导航步骤后,请run()依次依次执行它们:

casper.run();

脚注:回调/侦听器是Promise模式的实现。


在casperjs 1.0.0-RC1中,“ test-steps.js”显示的是[object DOMWindow]的集合,而不是函数定义字符串的集合。
starlocke 2012年

[object DOMWindow]集合仍然是1.0.0-RC4中的结果;我不知道这些功能定义去了哪里……
starlocke 2012年

1
最初我以为CasperJS正在执行将函数转换为DOMWindows的新技巧,但问题确实出在“ return this.toString()”与“ return step.toString()”之间–我提交了答案的修改。
starlocke 2012年

5
所谓的“堆栈”实际上不是队列吗?步骤是按顺序执行的,如果不是堆栈,我们是否不希望步骤3,步骤2,步骤1?
Reut Sharabani 2013年

1
我认为一定是这样的:您有很多步骤。您弹出一个步骤并对其进行评估。您创建一个空队列。由于当前步骤的处理而生成的任何步骤都将放入此队列中。步骤完成评估后,队列中所有生成的步骤都放在堆栈顶部,但保留其在队列中的顺序。(与以相反顺序推入堆栈相同)。
2015年

33

then() 仅注册一系列步骤。

run() 及其运行函数,回调和侦听器系列实际上是执行每个步骤的工作。

每当一个步骤完成,CasperJS将检查对3个标志:pendingWaitloadInProgress,和navigationRequested。如果这些标志中的任何一个为true,则不执行任何操作,直到以后再空闲(setInterval样式)。如果这些标志都不为真,则将执行下一步。

从CasperJS 1.0.0-RC4开始,存在一个缺陷,在某些基于时间的情况下,将在CasperJS有时间引发loadInProgressor 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()。请查看请求以获取更多信息,使用模式和最低测试文件。


1
关于blockStep恕我直言非常有用,很有见地和建议
Brian M. Hunt

我们仍在讨论“最终答案”解决方案...我希望一旦实现了“全局默认值”方面,CasperJS就会发挥作用。
starlocke 2012年

1
是的,请密切注意。:)
starlocke 2012年

我们对此有什么解决方案吗?如果是,那是什么?
Surender Singh Malik 2015年

非常感谢您对此进行解释。这种行为已经使我丧命了一年多,因为我对大量Ajax应用程序的Casper功能测试一直随机地失败。
brettjonesdev

0

根据CasperJS文档

then()

签名: then(Function then)

通过提供一个简单的函数,此方法是向堆栈中添加新导航步骤的标准方法:

casper.start('http://google.fr/');

casper.then(function() {
  this.echo('I\'m in your google.');
});

casper.then(function() {
  this.echo('Now, let me write something');
});

casper.then(function() {
  this.echo('Oh well.');
});

casper.run();

您可以根据需要添加任意多个步骤。请注意,当前Casper实例会自动this在步骤函数中为您绑定关键字。

要运行您定义的所有步骤,请调用run()方法,然后瞧。

注意:必须start()使用Casper实例才能使用该then()方法。

警告:then()在两种不同情况下将处理添加到的步进功能:

  1. 当执行上一步功能时,
  2. 当先前的主要HTTP请求已执行且页面 加载 ;

请注意,没有单一的定义 页面加载的;是何时触发DOMReady事件?是“所有请求都已完成”吗?是“正在执行所有应用程序逻辑”吗?还是“所有元素都被渲染”?答案总是取决于上下文。因此,为什么鼓励您始终使用waitFor()族方法对您实际期望的内容进行明确控制。

一个常见的技巧是使用waitForSelector()

casper.start('http://my.website.com/');

casper.waitForSelector('#plop', function() {
  this.echo('I\'m sure #plop is available in the DOM');
});

casper.run();

在幕后,其源代码Casper.prototype.then如下所示:

/**
 * Schedules the next step in the navigation process.
 *
 * @param  function  step  A function to be called as a step
 * @return Casper
 */
Casper.prototype.then = function then(step) {
    "use strict";
    this.checkStarted();
    if (!utils.isFunction(step)) {
        throw new CasperError("You can only define a step as a function");
    }
    // check if casper is running
    if (this.checker === null) {
        // append step to the end of the queue
        step.level = 0;
        this.steps.push(step);
    } else {
        // insert substep a level deeper
        try {
            step.level = this.steps[this.step - 1].level + 1;
        } catch (e) {
            step.level = 0;
        }
        var insertIndex = this.step;
        while (this.steps[insertIndex] && step.level === this.steps[insertIndex].level) {
            insertIndex++;
        }
        this.steps.splice(insertIndex, 0, step);
    }
    this.emit('step.added', step);
    return this;
};

说明:

换一种说法, then()安排导航过程中的下一步。

什么时候 then()被调用时,它被传递函数作为参数将被称为步骤。

它检查实例是否已启动,如果尚未启动,则显示以下错误:

CasperError: Casper is not started, can't execute `then()`.

接下来,它检查page对象是否为null

如果条件为真,Casper将创建一个新的 page对象。

之后,then()验证step参数以检查它是否不是函数。

如果参数不是函数,则会显示以下错误:

CasperError: You can only define a step as a function

然后,该功能检查Casper是否正在运行。

如果Casper没有运行, then()则将步骤追加到队列的末尾。

否则,如果Casper正在运行,它将插入比上一步更深的子步骤。

最后,该then()函数通过发出step.added事件结束,并返回Casper对象。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.