Jer*_*ohn 19 javascript asynchronous casperjs
我正在努力确定什么是异步,什么不是在运行CasperJS时,什么必须包含在then()语句中,以及什么时候将被评估.
我将遇到一个问题,与一个直通破坏语句,变量范围或evaluate()语句有关,我将开始在then()语句中包装我的所有代码......结果证明不成问题.
我注意到,当我单步执行时,我的代码运行在两个级别上,一个解析代码的评估级别,然后是then()语句.此外,我的打印语句有时会出现无法解释的顺序.
我的问题:这些then()语句如何实际排队?我已经阅读了文档,我有点理解.我想了解规则,并有一些切割和干燥的方法来确定什么是同步和什么是异步.
我甚至阅读过关于异步编码的书的部分内容,但似乎没有什么能真正解决CasperJS结构问题.有资源吗?
另外,在什么地方放置then()语句的最佳做法是什么?它们是应该在整个过程中自由地加上,还是应该在控制主要的casper.begin()函数中调用其他函数?
谢谢大家,我已经习惯了PHP.
Art*_* B. 64
经验法则:包含单词then并且wait是异步的所有CasperJS函数.这个陈述有很多例外.
then()做什么?CasperJS被组织为一系列处理脚本控制流的步骤.then()处理定义步骤结束的许多PhantomJS/SlimerJS事件类型.当then()被调用时,传递的函数被放入队列的步骤是一个简单的JavaScript数组.如果上一步结束,或者因为它是一个简单的同步函数,或者因为CasperJS检测到触发的特定事件,下一步将开始执行并重复此操作直到执行所有步骤.
所有这些步骤函数都绑定到casper对象,因此您可以使用引用该对象this.
以下简单脚本显示了两个步骤:
casper.start("http://example.com", function(){
this.echo(this.getTitle());
}).run();
Run Code Online (Sandbox Code Playgroud)
第一步是隐藏的异步("步进")open()调用start().该start()函数还采用可选的回调,这本身就是此脚本的第二步.
在执行第一步期间,页面被打开.页面完全加载后,PhantomJS会触发onLoadFinished事件,CasperJS会触发自己的事件并继续下一步.第二步是一个简单的完全同步功能,所以这里没有任何花哨的东西.完成后,CasperJS退出,因为没有更多步骤要执行.
此规则有一个例外:当函数传递给run()函数时,它将作为最后一步而不是默认退出执行.如果您不打电话exit()或die()在那里,您将需要终止该过程.
then()检测下一步必须等待?以下面的例子为例:
casper.then(function(){
this.echo(this.getTitle());
this.fill(...)
this.click("#search");
}).then(function(){
this.echo(this.getTitle());
});
Run Code Online (Sandbox Code Playgroud)
如果在步骤执行期间触发了表示加载新页面的事件,则CasperJS将等待页面加载,直到执行下一步骤.在这种情况下,触发了一个点击,它本身触发了底层浏览器的onNavigationRequested事件.CasperJS看到了这一点并使用回调暂停执行,直到加载下一页.其他类型的此类触发器可能是表单提交,或者甚至当客户端JavaScript使用window.open()/ 执行类似其自身重定向的操作时window.location.
当然,当我们谈论单页应用程序(使用静态URL)时,这会破坏.PhantomJS无法检测到例如在单击后呈现不同的模板,因此不能等到它完成加载(这可能需要一些时间从服务器加载数据).如果以下步骤依赖于新页面,则需要使用例如waitUntilVisible()查找要加载的页面唯一的选择器.
有些人称之为Promise,因为步骤可以链接.除了名称(then())和动作链之外,这就是相似之处的结束.没有结果通过CasperJS中的步骤链从回调传递到回调.您可以将结果存储在全局变量中,也可以将其添加到casper对象中.然后只有有限的错误处理.遇到错误时,CasperJS将在默认配置中死亡.
我更喜欢将其称为Builder模式,因为一旦调用就会执行,run()之前的每次调用都只是将步骤放入队列(参见第1个问题).这就是在步骤函数之外编写同步函数没有意义的原因.简而言之,它们的执行没有任何背景.该页面甚至没有开始加载.
当然,通过将其称为构建器模式,这不是全部事实.步骤可以嵌套,这实际上意味着如果您在另一个步骤中安排一个步骤,它将在当前步骤之后以及在已经从当前步骤调度的所有其他步骤之后被放入队列中.(这是很多步骤!)
以下脚本很好地说明了我的意思:
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();
Run Code Online (Sandbox Code Playgroud)
第一个数字显示脚本中同步代码片段的位置,第二个数字显示实际执行/打印位置,因为它echo()是同步的.
重点:
为避免混淆并且难以发现问题,请始终在同步功能之后单步调用异步函数.如果看起来不可能,请分成多个步骤或考虑递归.
waitFor()工作怎么样?waitFor()是wait*家庭中最灵活的功能,因为每个其他功能都使用这个功能.
waitFor()以最基本的形式(只传递一个检查功能而不是其他)的计划一步.check传递给它的函数会被重复调用,直到满足条件或达到(全局)超时.当另外传递a then和/或onTimeoutstep函数时,将在这些情况下调用它.
重要的是要注意,如果waitFor()超时,当你没有传入onTimeout回调函数时,脚本将停止执行,这实际上是一个错误捕获函数:
casper.start().waitFor(function checkCb(){
return false;
}, function thenCb(){
this.echo("inner then");
}, null, 1000).then(function() {
this.echo("outer");
}).run();
Run Code Online (Sandbox Code Playgroud)
从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执行脚本的方式.当我们打破控制流程时,我们需要管理第二个流程(甚至更多).这将极大地复杂化脚本的开发和可维护性.
例如,您想要调用在某处定义的异步函数.让我们假设没有办法以这种方式重写函数,它是同步的.
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();
Run Code Online (Sandbox Code Playgroud)
现在我们有两个相互依赖的流程.
直接拆分流的其他方法是使用JavaScript函数setTimeout()和setInterval().自CasperJS提供以来waitFor(),没有必要使用它们.
当控制流必须合并回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();
Run Code Online (Sandbox Code Playgroud)
从技术上讲,测试仪模块中没有任何异步.调用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();
});
});
Run Code Online (Sandbox Code Playgroud)
最好坚持在其中嵌套一个完整的流begin(),因为start()和run()多个流之间的调用不会混合.这使您可以为每个文件使用多个完整的测试用例.
笔记:
| 归档时间: |
|
| 查看次数: |
7244 次 |
| 最近记录: |