navigator.sendBeacon()

Whenever you want to send a small amount of data to the server from client-side JS you almost always use AJAX, right? (Or, if you’re old enough, maybe a JS generated <img />.) For example, maybe you want to benchmark user actions or maybe you have your own special analytics. Whatever the case, your browser cannot distinguish between your important but optional tracking/benchmark JS and your application’s 100% required JS. From the browser’s perspective they both carry the same importance and will be treated as show-stopping code that must happen or the world will end. Okay, maybe the world won’t end, but the webpage must execute the code and block until that code happens.

Enter navigator.sendBeacon() which works pretty much the same as ajax.post() except that you don’t get to specify a callback and it always works asynchronously.

The goals of this method from the current editor’s draft of the spec are

  • Beacon requests are prioritized to avoid competing with time-critical operations and higher priority network requests.
  • Beacon requests may be efficiently coalesced by the user agent to optimize energy use on mobile devices.
  • Beacon requests are guaranteed to be initiated before page is unloaded and are allowed to run to completion without requiring blocking requests or other techniques that block processing of user interactive events.

A word of caution

One very important thing to note is that sendBeacon() does not actually send the beacon, it only queues the request for later. This is a small but important difference. The purpose of the sendBeacon() method is to send a small amount of data to the server and if your payload is too big then the user’s browser might reject the beacon call. When you call sendBeacon() you should always check the return value and if that fails you should fallback to an AJAX request.

Sample code

Here’s a basic sample of how to use it. It assumes you’ve already got your favorite AJAX code.

/*jslint maxparams: 4, maxdepth: 4, maxstatements: 25, maxcomplexity: 10 */
/*global ajax */
(function( )
{
    'use strict';                         //Force strict mode

    var

        init = function()
        {
            var
                //Whatever our data is
                data = {
                            a: 'cheese',
                            b: 'beer',
                },

                //Your URL
                url = '/your/endpoint/here',

                //Assume that we need to use AJAX
                useAjax = true
            ;

            //If we have sendBeacon() enabled
            if( navigator.sendBeacon )
            {
                //Try queuing the beacon.
                //NOTE: This does NOT actually send right here, only queues!!
                if( navigator.sendBeacon( url, data ) )
                {
                    //It queued correctly, we don't need to fallback to AJAX
                    useAjax = false;
                }
            }

            if( useAjax )
            {
                //Assuming you've got this method already setup elswhere with the signature:
                //ajax.post = function( url, data, callback, sync ){}
                ajax.post( url, data, function(){}, true );
            }
        }
    ;

    //Kick everything off
    init( );
}
()
);

How much is too big?

Right now it appears that Edge and Firefox have a maximum of 64KB for each beacon and Chrome has a combined maximum of 64KB for (essentially) the page. This can be seen in both the editor’s commit notes as well as through some testing found on Stack Overflow.