Кешируем ассинхронные запросы

Когда на одной странице несколько ajax-запросов выполняются с одними и теми же параметрами и допустимо закешировать результат, то можно сделать это так:

var subscribers = {};

function getSubscribers(id) {
    var dfd = $.Deferred(), promise = dfd.promise();
    if (typeof subscribers[id] !== "undefined") {
        subscribers[id].done(function (count) {
            dfd.resolveWith(promise, [count]);
        });
    } else {
        subscribers[id] = promise;
        $.ajax({
            url: "/subscribers",
            type: "get",
            dataType: "text",
            data: {
                id: id
            }
        })
        .done(function (count) {
            dfd.resolveWith(promise, [count]);
        });
    }
    return promise;
}

Функция вернет promise-объект, который будет разрешен, когда будет известно количество подписчиков.

Если во время запроса на сервер потребуется еще раз получить количество подписчиков, то в этом случае новый ajax-запрос не будет создаваться. Мы подписываемся на результат уже запущенного запроса и ожидаем его успешного завершения.

Все последующие вызовы функции будут возвращать закешированный результат.

Обновление: Пересмотрел код и понял, что его можно оптимизировать, избавившись от промежуточного отложенного объекта. Заодно обобщил функцию, чтобы её можно было использовать для разных запросов.

var cacheable = (function () {
    var promises = {};
    return function (opts) {
        var key = $.param({
                url: opts.url || "",
                type: (opts.type || "get").toLowerCase(),
                data: opts.data || {}
            });
        return promises[key] ? promises[key] :
                (promises[key] = $.ajax(opts).promise());
    };
}());

cacheable({
    url: "/subscribers",
    type: "get",
    dataType: "text",
    data: {
        id: id
    }
});