newspaint

Documenting Problems That Were Difficult To Find The Answer To

PhantomJS window.setTimeout Appears to be Ignored

This could equally apply to a Node.JS program as much as a PhantomJS program.

You’ve got a JavaScript program that has many setTimeout() or window.setTimeout() calls. Yet your program appears to be failing for no apparent reason. Even after scrutinising your program – perhaps running it through esvalidate and concluding that there are no syntax errors – your script just appears to terminate without any reason. And those terminations happen before an expected setTimeout() callback is due to trigger.

You wouldn’t be alone if you suffered this problem. Quite a few people have contributed to a Github PhantomJS issue on this topic. Many assuming their call to setTimeout() failed.

Here is an example script that appears to terminate without reason before the final window.setTimeout() call:

function step3() {
    console.log( "+step3()" );
    window.setTimeout(
        function () {
            console.log( "-exiting phantomJS" );
            phantom.exit(1);
        }, 1000
    );
}

function step2() {
    console.log( "+step2()" );
    window.setTimeout(
        function () {
            step3();
            phantom.exit(1);
        }, 1000
    );
}

function step1() {
    console.log( "+step1()" );
    window.setTimeout(
        function () {
            step2();
        }, 1000
    );
}

step1();

When run this script outputs:

user@host:~$ phantomjs /tmp/test.js
+step1()
+step2()
+step3()

This script appeared to terminate without displaying the final “-exiting phantomJS” log message. So what went wrong? After all, step3() was executed as expected.

The problem is that an unwanted call to phantom.exit() was made after the call to step3() in step2(). What actually happens is that step3() is called, and the setTimeout() is made, but then the function returns back and executes the call to phantom.exit() – which terminates the script before the final setTimeout() callback has a chance to trigger.

How Can I Avoid This Silent Killer?

You can protect yourself from having this problem in the future. Never, never call phantom.exit() directly from your script. Instead add the following function and call this instead whenever you want to terminate your script:

function quit( reason, value ) {
    console.log( "QUIT: " + reason );
    phantom.exit( value );
}

Why do this? If your program aborts you want to know WHY. If you provide a unique message at every point in which your program can exit then you can quickly identify when a mistaken quit() call has interrupted your expected setTimeout() callback.

Here’s the fixed program:

function quit( reason, value ) {
    console.log( "QUIT: " + reason );
    phantom.exit( value );
}

function step3() {
    console.log( "+step3()" );
    window.setTimeout(
        function () {
            console.log( "-exiting phantomJS" );
            quit( "finished", 0 );
        }, 1000
    );
}

function step2() {
    console.log( "+step2()" );
    window.setTimeout(
        function () {
            step3();
            // phantom.exit(1); WAS CAUSING PROBLEM
        }, 1000
    );
}

function step1() {
    console.log( "+step1()" );
    window.setTimeout(
        function () {
            step2();
        }, 1000
    );
}

step1();

This outputs:

user@host:~$ phantomjs /tmp/test.js
+step1()
+step2()
+step3()
-exiting phantomJS
QUIT: finished

One response to “PhantomJS window.setTimeout Appears to be Ignored

  1. Juan August 10, 2014 at 8:27 am

    Thank you very much for sharing it… I’ve been through https://github.com/ariya/phantomjs/issues/10832 and nobody seemed to have the answer.
    Great you posted it

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: