我正在尝试将"Q"承诺库与PhantomJS一起使用,但下面的代码不起作用。
app.evaluate_page=function(page){
var deferred = q.defer();
console.log("Before the page evaluation");
page.evaluate(function(){
deferred.resolve(page);
});
return deferred.promise;
};
以下是错误
Before the page evaluation
ReferenceError: Can't find variable: deferred
phantomjs://webpage.evaluate():2
phantomjs://webpage.evaluate():3
phantomjs://webpage.evaluate():3
看起来它无法找到在外部作用域中定义的延迟变量,这很奇怪。相同类型的代码适用于具有回调的其他方法,如page.open方法。
以下代码只是按预期工作,不确定为什么上面的代码不能。
app.openPage = function(options){
var deferred = q.defer();
page.open(options.url,function(status){
if(status!=="success"){
deferred.reject(new Error("Page could not be loaded at "+ options.url ));
}
else {
deferred.resolve(page);
}
});
return deferred.promise;
};
page.evaluate
在浏览器进程中运行,无法访问您的变量。
您同步拥有page
变量,因此您不需要此处的承诺。
正如@slaks提到的,page.evaluate
回调函数在沙盒环境中运行。
官方文件明确指出page.evaluate
:
在网页上下文中计算给定函数。这 执行是沙盒化的,网页无法访问幻像 对象,它无法探测自己的设置。
然而,你可以传入和传出参数,但正如官方文档所述:
注意:参数和返回值的求值函数必须 是一个简单的原始对象。经验法则:如果可以的话 通过 JSON 序列化,那就没问题了。
截至目前(2016年10月),深入研究该主题,这似乎是一个悬而未决的问题。
- GitHub上有一个关于使用
page.evaluate
启用承诺的未决问题:等待对承诺模式的支持 ·问题 #14166 ·Ariya/Phantomjs - 有一个实验性 API 可以从网页上下文启用回调函数:onCallback |幻影
- 等待网页准备就绪的官方方法是扩展 Waitfor.js示例
我使用第三种方法的个人混合。
这是我main.js
文件:
'use strict';
var wasSuccessful = phantom.injectJs('./lib/waitFor.js');
var page = require('webpage').create();
page.open('http://foo.com', function(status) {
if (status === 'success') {
page.includeJs('https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js', function() {
waitFor(function() {
return page.evaluate(function() {
if ('complete' === document.readyState) {
return true;
}
return false;
});
}, function() {
var fooText = page.evaluate(function() {
return $('#foo').text();
});
phantom.exit();
});
});
} else {
console.log('error');
phantom.exit(1);
}
});
还有lib/waitFor.js
文件(它只是从 phantomjs waitfor.js
示例中复制和粘贴waifFor()
函数):
function waitFor(testFx, onReady, timeOutMillis) {
var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3000, //< Default Max Timout is 3s
start = new Date().getTime(),
condition = false,
interval = setInterval(function() {
if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
// If not time-out yet and condition not yet fulfilled
condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
} else {
if(!condition) {
// If condition still not fulfilled (timeout but condition is 'false')
console.log("'waitFor()' timeout");
phantom.exit(1);
} else {
// Condition fulfilled (timeout and/or condition is 'true')
// console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condi>
clearInterval(interval); //< Stop this interval
}
}
}, 250); //< repeat check every 250ms
}
此方法不是异步的,但至少我可以确保在尝试使用它们之前已加载所有资源。