【问题标题】:What Does 'Then' Really Mean in CasperJSCasperJS 中“那么”的真正含义是什么
【发布时间】:2012-07-21 05:18:44
【问题描述】:

我正在使用 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 语句之前运行,我不知道为什么。

【问题讨论】:

  • 我仍在寻找对 casperjs 的一般 flow 的解释,但我发现您基本上无法从 evaluate 调用中引用 casper。 (即您不能打开新的 url、日志、回显等)。所以在我的情况下,evaluate 被调用但无法与外界交互。
  • 我想知道完全相同的事情,但懒得问。好问题!
  • evaluate() 用于在“浏览器”中运行的代码,在 phantomjs 正在浏览的页面的 DOM 中。所以那里没有casper.open,但可能有jQuery。所以你的例子没有意义,但我仍然想知道 then() 究竟做了什么。

标签: javascript casperjs


【解决方案1】:

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

  1. 等待上一步 - 如果有的话 - 正在执行
  2. 正在等待加载请求的 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 pattern 的实现。

【讨论】:

  • 在 casperjs 1.0.0-RC1 中,“test-steps.js”显示的是 [object DOMWindow] 的集合,而不是函数定义字符串的集合。
  • [object DOMWindow] 集合仍然是 1.0.0-RC4 中的结果;我想知道那些函数定义去了哪里......
  • 我最初以为 CasperJS 是在做一个将函数转换为 DOMWindows 的新技巧,但问题真的是“返回 this.toString()”与“返回 step.toString()”——我提交了编辑答案。
  • 所谓的“栈”不就是一个队列吗?这些步骤是按顺序执行的,如果它是一个堆栈,我们不会期望第 3 步、第 2 步、第 1 步吗?
  • 我想一定是这样的:你有一堆步骤。你弹出一个步骤并评估它。您创建一个空队列。由于当前步骤的处理而生成的任何步骤都会放入此队列中。当步骤完成评估后,队列中所有生成的步骤都被放在堆栈顶部,但保留它们在队列中的顺序。 (与以相反顺序压入堆栈相同)。
【解决方案2】:

then() 只是注册了一系列步骤。

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

每当一个步骤完成时,CasperJS 将检查 3 个标志:pendingWait、loadInProgress 和 navigationRequested。如果这些标志中的任何一个为真,则什么也不做,闲置到稍后时间(setInterval 风格)。如果这些标志都不为真,则将执行下一步。

从 CasperJS 1.0.0-RC4 开始,存在一个缺陷,在某些基于时间的情况下,“尝试执行下一步”方法将在 CasperJS 有时间提出 @987654328 之一之前触发@ 或 navigationRequested 标志。解决方案是在离开预期会引发这些标志的任何步骤之前升起这些标志之一(例如:在要求casper.click() 之前或之后升起一个标志),可能像这样:

(注意:这只是说明性的,更像是伪代码而不是正确的 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()
}

为了将该解决方案包含在一行代码中,我在此 github 中引入了 blockStep() pull request,扩展了 click() 和 clickLabel(),以帮助确保我们在使用时获得预期的行为then()。查看请求以获取更多信息、使用模式和最小测试文件。

【讨论】:

  • 对blockStep,恕我直言,非常有帮助和深刻的见解和建议
  • 我们仍在讨论“最终答案”的解决方案......我希望一旦我实现了“全局默认值”方面,CasperJS 将成为拉动。
  • 所以,是的,请留意它。 :)
  • 我们有什么解决办法吗?如果是的话是什么?
  • 非常感谢您的解释。这种行为已经害了我一年多,因为我对 Ajax 繁重的应用程序的 Casper 功能测试一直随机失败。
【解决方案3】:

根据CasperJS Documentation:

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();

在幕后,source code for 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 对象。

【讨论】:

    猜你喜欢
    • 2013-02-02
    • 2017-05-06
    • 2012-03-30
    • 2011-10-10
    • 2012-08-03
    • 2012-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多