Implementing waitFor functionality with phantomjs-node

前端 未结 3 1696
囚心锁ツ
囚心锁ツ 2021-01-02 01:10

I have tried and tested - with success - the phantomjs example waitFor. However, I am having difficulty implementing it via the phantomjs-node module primarily because

相关标签:
3条回答
  • 2021-01-02 01:19

    I recently created a fairly simple node module to port waitFor over to node: 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);
            }
        );
    
    }
    
    0 讨论(0)
  • 2021-01-02 01:29

    I've written an alternative for phantomjs-node called phridge. Instead of turning all function calls and assignments into async operations it just executes the whole function inside PhantomJS.

    I think your problem could be accomplished like this:

    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
        });
    
    0 讨论(0)
  • 2021-01-02 01:36

    Ran into this problem today, thought I'd share my solution.

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

    The first step is creating a new wait function. It takes the same parameters as the original waitFor function, but works a little differently. Instead of using an interval, we have to run the wait function recursively, after the callback from the test function testFx has been triggered. Also, note that you don't actually need to pass in a value for start, as it gets set automatically.

      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
    

    In this example, I'm setting the callback for testFx function as the callback to page.evaluate, which returns a true/false value based on whether or not it was able to find some element on the page. Alternatively, you could create your callback for page.evaluate and then trigger the testFx callback from it, as shown below:

      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
    
    0 讨论(0)
提交回复
热议问题