用phantomjs-node实现waitForfunction
我已经尝试和testing – 成功 – phantomjs示例waitFor 。 但是,我很难通过phantomjs-node模块实现它,主要是因为page.evaluate
在callback中被评估。
PhantomJS实现
page.open("http://twitter.com/#!/sencha", function () { waitFor(function() { // This here is easy to do as the evaluate method returns immediately return page.evaluate(function() { return $("#signin-dropdown").is(":visible"); }); }, function() { console.log("The sign-in dialog should be visible now."); phantom.exit(); }); } });
但是,对于phantomjs-node ,评估函数在callback中获取返回的数据:
page.evaluate( function(){ /* return thing */ }, function callback(thing) { /* write code for thing */ } )
使用phantomjs-node ,我只能在元素可见后才能在页面上运行一个函数。
万一上面的链接死了,这里是waitFor函数的实现
/** * Wait until the test condition is true or a timeout occurs. Useful for waiting * on a server response or for a ui change (fadeIn, etc.) to occur. * * @param testFx javascript condition that evaluates to a boolean, * it can be passed in as a string (eg: "1 == 1" or "$('#bar').is(':visible')" or * as a callback function. * @param onReady what to do when testFx condition is fulfilled, * it can be passed in as a string (eg: "1 == 1" or "$('#bar').is(':visible')" or * as a callback function. * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used. */ 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 condition is fulfilled clearInterval(interval); //< Stop this interval } } }, 250); //< repeat check every 250ms };
提前致谢。
今天遇到这个问题,以为我会分享我的解决scheme。
// custom helper function function wait(testFx, onReady, maxWait, start) { var start = start || new Date().getTime() if (new Date().getTime() - start < maxWait) { testFx(function(result) { if (result) { onReady() } else { setTimeout(function() { wait(testFx, onReady, maxWait, start) }, 250) } }) } else { console.error('page timed out') ph.exit() } }
第一步是创build一个新的wait
function。 它采用与原始的waitFor
函数相同的参数,但工作方式稍有不同。 在testing函数testFx
的callback被触发之后,我们不必使用间隔,而是recursion地运行wait
函数。 另外,请注意,您实际上并不需要传入start
值,因为它会自动设置。
wait(function (cb) { return page.evaluate(function () // check if something is on the page (should return true/false) return something }, cb) }, function () { // onReady function // code }, 5000) // maxWait
在这个例子中,我将testFx
函数的callback函数设置为testFx
的callback函数,根据是否能够在页面上find某个元素返回true / false值。 或者,您可以为page.evaluate
创buildcallback,然后testFx
触发testFx
callback,如下所示:
wait(function (cb) { return page.evaluate(function () // check if something is on the page (should return true/false) return something }, function(result) { var newResult = doSomethingCrazy(result) cb(newResult) } }, function () { // onReady function // code }, 5000) // maxWait
我已经写了一个替代phantomjs节点称为phridge 。 它不是将所有的函数调用和赋值都转换成asynchronous操作,而是执行PhantomJS中的全部函数。
我认为你的问题可以这样完成:
phridge.spawn() .then(function (phantom) { return phantom.openPage(url); }) .then(function (page) { return page.run(selector, function (selector, resolve, reject) { // this function runs inside PhantomJS bound to the webpage instance var page = this; var intervalId = setInterval(function () { var hasBeenFound = page.evaluate(function (selector) { return Boolean(document.querySelector(selector)); }, selector); if (hasBeenFound === false && /* check if there is still some time left */) { // wait for next interval return; } clearInterval(intervalId); if (hasBeenFound) { resolve(); } else { reject(new Error("Wait for " + selector + " timeout")); } }, 100); }); }) .then(function () { // element has been found }) .catch(function (err) { // element has not been found });
我最近创build了一个相当简单的节点模块,将waitFor转移到节点上: https ://gist.github.com/joseym/1d01edbcc40a7698f55a#file-phantomjs-waitfor-js
var async = require('async'); module.exports = waitFor; /** * waitFor port used with * @see {@link https://github.com/ariya/phantomjs/blob/master/examples/waitfor.js} * @see {@link https://github.com/sgentle/phantomjs-node} * @callback testFx - Test function, will repeat until true or timeout limit is reached * @callback onReady - Fires if/when `testFx` passes. * @param {(number|boolean|string)} [timeOut=false] - If defined and falsey or string value of`forever` * then `waitFor` will run until `testFx` passes without * timing out, otherwise pass a number in miliseconds. */ function waitFor(testFx, onReady, timeOut) { var maxtimeOutMillis = typeof timeOut !== 'undefined' ? timeOut : 5000 // Default Max Timout is 5s if not defined , start = new Date().getTime() , isAsync = testFx.length > 0 , passing = undefined ; async.until( function Test() { return typeof passing !== 'undefined'; }, function Action(cb) { setTimeout(function(){ if (!maxtimeOutMillis || maxtimeOutMillis == 'forever' || new Date().getTime() - start < maxtimeOutMillis) { // If a callback is passed to `testFx` we'll handle that. function useCallback(){ passing = arguments[0] return cb(); }; passing = (function(){ return (typeof(testFx) === "string" ? eval(testFx) : testFx).apply(this, arguments); })(isAsync ? useCallback : undefined); if(!isAsync) cb(); } else { return cb(new Error('`waitFor` timeout')); } }, 250); }, function Done(err) { return (function(){ return (typeof(onReady) === "string" ? eval(onReady) : onReady).apply(this, arguments); })(err, passing); } ); }