【问题标题】:Implementing waitFor functionality with phantomjs-node使用 phantomjs-node 实现 waitFor 功能
【发布时间】:2014-03-10 04:10:45
【问题描述】:

我已经尝试并测试过 - 成功 - phantomjs 示例waitFor。但是,我很难通过phantomjs-node 模块实现它,主要是因为page.evaluate 在回调中被评估。

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,评估函数在回调中获取返回的数据:

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 (e.g.: "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 (e.g.: "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
};

提前致谢。

【问题讨论】:

    标签: javascript node.js phantomjs


    【解决方案1】:

    今天遇到这个问题,想分享一下我的解决方案。

      // 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()
        }
      }
    

    第一步是创建一个新的wait 函数。它采用与原始 waitFor 函数相同的参数,但工作方式略有不同。在触发了来自测试函数testFx 的回调之后,我们必须递归运行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 函数的回调设置为page.evaluate 的回调,它根据是否能够在页面上找到某个元素来返回真/假值。或者,您可以为page.evaluate 创建回调,然后从中触发testFx 回调,如下所示:

      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
    

    【讨论】:

    • 等待功能类似于 CasperJS 的实现 :)
    • @ameensol 你能添加一个正在使用的函数的例子吗?
    • 这个功能对我不起作用。 evaluate的返回值是一个promise,不是js求值的结果。
    【解决方案2】:

    我为phantomjs-node 编写了一个替代方案,名为phridge。它不是将所有函数调用和赋值转换为异步操作,而是在 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
        });
    

    【讨论】:

    • 如何使用phridge生成PDF?
    • 和普通的phantomjs没什么区别,phridge只是node和phantomjs之间的桥梁。
    【解决方案3】:

    我最近创建了一个相当简单的节点模块来将 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);
            }
        );
    
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多