« Back to Index

Polling example with back pressure handling

View original Gist on GitHub

polling.js

define(['module/bootstrap', 'module/dispatch', 'config'], function(news, dispatch, config) {
    var URL = config.dynamic.broker;
    var cb;

    function seconds(secs) {
        return secs * 1000;
    }

    function minutes(mins) {
        return mins * seconds(60);
    }

    function incrementDelay() {
        if (delay < THRESHOLD) {
            delay *= 2;
        }

        return delay; // we return a value for the sake of our `dispatch` method called by `statusChecks`
    }

    var statusChecks = dispatch(
        function(status) { return status === 304       ? incrementDelay()   : null; },
        function(status) { return status === 200       ? delay = seconds(5) : null; },
        function(status) { return status === 420       ? true               : null; },
        function(status) { return status === 'timeout' ? true               : null; }
    );

    function isSafeToCallback(status, rest) {
        return status ? executeCallback(rest[0]) : null;
    }

    function trackTimeout() {
        failCount++;

        return disablePolling(failCount > FAIL_THRESHOLD);
    }

    function tooManyRequests(timeout, resultFromStatusCheck) {
        return timeout ? trackTimeout() : resultFromStatusCheck;
    }

    function disablePolling(status) {
        return status ? true : false;
    }

    function delayPolling(disabled) {
        if (!disabled) {
            incrementDelay();
            poll(endpoint(URL));
        }
    }

    function executeCallback(data) {
        cb(data);
        poll(endpoint(URL));
    }

    function success(data, status, xhr) {
        isSafeToCallback(
            statusChecks(xhr.status), data
        );
    }

    // This alias helps make a call to statusChecks inside failure fn clearer
    // Otherwise it would look like we're calling the same function twice
    var checkForTimeout = statusChecks;

    function failure(xhr, status, error) {
        delayPolling(
            disablePolling(
                tooManyRequests(
                    checkForTimeout(status), statusChecks(xhr.status)
                )
            )
        );
    }

    function postDataAssetTemplate(data) {
        return {
            "type": "asset",
            "payload": {
                "component_id": data.id,
                "options": data.opts
            }
        };
    }

    function generateJsonData() {
        var obj  = config.dynamic.components;
        var data = { requests: [] };

        for (var prop in obj) {
            data.requests.push(
                postDataAssetTemplate(obj[prop])
            );
        }

        return JSON.stringify(data);
    }

    function endpoint(url) {
        return (function(url) {
            return function() {
                news.$.ajax(url, {
                    processData : false,
                    type        : 'post',
                    contentType : 'application/json',
                    data        : generateJsonData(),
                    timeout     : seconds(5)
                }).then(success, failure);
            };
        }(url));
    }

    function poll(endpoint) {
        return window.setTimeout(endpoint, delay);
    }

    // Constants
    var THRESHOLD      = seconds(20);
    var FAIL_THRESHOLD = 5;

    // Mutables
    var delay     = seconds(5);
    var failCount = 0;

    return function(callback) {
        cb = callback;
        poll(endpoint(URL));
    };
});

// Fuzzying the delay was a bit confusing so left it for now