Heartbeat API long polling

The Hearbeat API works by periodically sending an XHR request to the server, with hooks available for WordPress/plug-ins/themes to attach data to be returned to the client. Once the data is returned, a new beat is scheduled. The speed (or ‘pulse’) of the heartbeat may be tempered, either server-side or client side in reaction to events (e.g. user inactivity).

Long polling

Long polling works in a similar way but (source: Wikipedia)

If the server does not have any information available for the client when the poll is received, instead of sending an empty response, the server holds the request open and waits for response information to become available. Once it does, the server immediately sends an HTTP/S response to the client, completing the open HTTP/S Request.

So rather than periodically sending requests and hoping we get something back, we send one request, and the server sends the data back when its ready. Once it does, we immediately initiate a new XHR request, hence the interval is 0.

Health warning

As mentioned in the question’s comments, long polling is a ‘latent’ feature in WordPress. The Heartbeat API makes provision for it, without actually implementing it. The reasons for it not being implemented are covered in track ticket #23216, and I recommend you read the discussion.

In short however, the disadvantages/potential problems include:

  • Caching – Long poll requests load WordPress. Database queries (including options, posts etc) are cached. If your script is waiting for data to be changed, you’ll need to mindful that you’re not checking cached data which won’t change unless you clear the cache.
  • Server settings – Long poll requests in this context can be thought of a (potentially) very very long page request. The example below kills itself after 20 seconds, but your host may kill it off long before that, rendering long polling fairly impotent.
  • Server resources – Each visitor will be tying up server resources, almost constantly.

For these reasons I recommend you only use it in a controlled environment (e.g. you control the server, and which plug-ins/themes are running). But with that level of control, you may want to consider using WebSockets instead.

Setting up long-polling

1. Setting the interval

First you need to set the interval to ‘long-polling’. Effectively this means a 0 interval.

function wpse162220_heartbeat_settings( $settings ) {
    $settings['interval'] = 'long-polling';
    return $settings;
}
add_filter( 'heartbeat_settings', 'wpse162220_heartbeat_settings' );

2. Add a ‘long-poll listener’

This is a callback which allows you to attach data to be returned to the client. Copying Nacin’s example from the track ticket we have a 2-second period, and wait up to 20 seconds.

function wpse162220_poll() {

    for ( $i = 0; $i < 10; $i++ ) {

        $val = apply_filters( 'wpse162220_poll', array() );

        if ( ! empty($val) ) {
            echo json_encode($val);
            exit;
        }

        sleep(2);
    }

    echo '0';
    exit;
}

add_action( 'heartbeat_tick', 'wpse162220_poll' );
add_action( 'heartbeat_nopriv_tick', 'wpse162220_poll' ); 

3. (a) Attach data

This is a ‘dummy’ function which just waits until the third iteration of the loop in the previous section and then adds some data

function wpse162220_populate_data( $value ){
    static $counter = 0;

    $counter++;

    if( $counter == 3 ){
        $value['wpse162220'] = 'foobar';
    }

    return $value;

}
add_filter( 'wpse162220_poll', 'wpse162220_populate_data' );

3. (b) Display the received data

I’ve been lazy here and printed the javascript straight to the page. You would normally go through the whole rigmarole of registering & enqueueing a javascript file.

All this does is print the response to the console.

function wpse162220_print_javascript(){
    ?>  
    <script>
    jQuery(document).ready( function($) {
        $(document).on( 'heartbeat-tick.wpse162220', function( event, data ) {
            if ( data.hasOwnProperty( 'wpse162220' ) ) {
                console.log( data );
            }
        });
    });
    </script>
    <?php
}
add_action( 'admin_print_footer_scripts', 'wpse162220_print_javascript', 999 );

You should see it print something like

 Object {wpse162220: "foobar"} 

to the console every ~4 seconds.

Leave a Comment