用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一个新的waitfunction。 它采用与原始的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触发testFxcallback,如下所示:

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