Mettre en cache une réponse jquery ajax dans javascript / browser

96

Je voudrais activer la mise en cache d'une réponse ajax dans javascript / navigateur.

À partir de la documentation jquery.ajax :

Par défaut, les demandes sont toujours émises, mais le navigateur peut diffuser des résultats hors de son cache. Pour interdire l'utilisation des résultats mis en cache, définissez le cache sur false. Pour que la demande signale un échec si l'actif n'a pas été modifié depuis la dernière demande, définissez ifModified sur true.

Cependant, aucune de ces adresses ne force la mise en cache.

Motivation: Je souhaite mettre des $.ajax({...})appels dans mes fonctions d'initialisation, dont certaines demandent la même url. Parfois, j'ai besoin d'appeler l'une de ces fonctions d'initialisation, parfois j'en appelle plusieurs.

Donc, je veux minimiser les demandes au serveur si cette URL particulière a déjà été chargée.

Je pourrais lancer ma propre solution (avec quelques difficultés!), Mais j'aimerais savoir s'il existe une manière standard de le faire.

cammil
la source
Je n'aurais pas pensé qu'il était difficile de suivre les URL que vous avez déjà chargées et de stocker les résultats dans cette liste. Ensuite, vous pouvez vérifier vos URL avant de passer un appel AJAX. Voila - vous avez votre propre cache de base.
vous pouvez ajouter l'en-tête cache-control et expires à votre réponse sur le serveur, de sorte que votre serveur ne devrait être appelé qu'après le délai que vous avez configuré dans ces valeurs
hereandnow78

Réponses:

143

cache:true fonctionne uniquement avec les requêtes GET et HEAD.

Vous pouvez lancer votre propre solution comme vous l'avez dit avec quelque chose du genre:

var localCache = {
    data: {},
    remove: function (url) {
        delete localCache.data[url];
    },
    exist: function (url) {
        return localCache.data.hasOwnProperty(url) && localCache.data[url] !== null;
    },
    get: function (url) {
        console.log('Getting in cache for url' + url);
        return localCache.data[url];
    },
    set: function (url, cachedData, callback) {
        localCache.remove(url);
        localCache.data[url] = cachedData;
        if ($.isFunction(callback)) callback(cachedData);
    }
};

$(function () {
    var url = '/echo/jsonp/';
    $('#ajaxButton').click(function (e) {
        $.ajax({
            url: url,
            data: {
                test: 'value'
            },
            cache: true,
            beforeSend: function () {
                if (localCache.exist(url)) {
                    doSomething(localCache.get(url));
                    return false;
                }
                return true;
            },
            complete: function (jqXHR, textStatus) {
                localCache.set(url, jqXHR, doSomething);
            }
        });
    });
});

function doSomething(data) {
    console.log(data);
}

Travail du violon ici

EDIT: à mesure que cet article devient populaire, voici une réponse encore meilleure pour ceux qui veulent gérer le cache de timeout et vous n'avez pas non plus à vous soucier de tout le désordre dans $ .ajax () car j'utilise $ .ajaxPrefilter () . Maintenant, le réglage {cache: true}suffit pour gérer correctement le cache:

var localCache = {
    /**
     * timeout for cache in millis
     * @type {number}
     */
    timeout: 30000,
    /** 
     * @type {{_: number, data: {}}}
     **/
    data: {},
    remove: function (url) {
        delete localCache.data[url];
    },
    exist: function (url) {
        return !!localCache.data[url] && ((new Date().getTime() - localCache.data[url]._) < localCache.timeout);
    },
    get: function (url) {
        console.log('Getting in cache for url' + url);
        return localCache.data[url].data;
    },
    set: function (url, cachedData, callback) {
        localCache.remove(url);
        localCache.data[url] = {
            _: new Date().getTime(),
            data: cachedData
        };
        if ($.isFunction(callback)) callback(cachedData);
    }
};

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
    if (options.cache) {
        var complete = originalOptions.complete || $.noop,
            url = originalOptions.url;
        //remove jQuery cache as we have our own localCache
        options.cache = false;
        options.beforeSend = function () {
            if (localCache.exist(url)) {
                complete(localCache.get(url));
                return false;
            }
            return true;
        };
        options.complete = function (data, textStatus) {
            localCache.set(url, data, complete);
        };
    }
});

$(function () {
    var url = '/echo/jsonp/';
    $('#ajaxButton').click(function (e) {
        $.ajax({
            url: url,
            data: {
                test: 'value'
            },
            cache: true,
            complete: doSomething
        });
    });
});

function doSomething(data) {
    console.log(data);
}

Et le violon ici ATTENTION, ne fonctionne pas avec $ .Deferred

Voici une implémentation fonctionnelle mais imparfaite fonctionnant avec différé:

var localCache = {
    /**
     * timeout for cache in millis
     * @type {number}
     */
    timeout: 30000,
    /** 
     * @type {{_: number, data: {}}}
     **/
    data: {},
    remove: function (url) {
        delete localCache.data[url];
    },
    exist: function (url) {
        return !!localCache.data[url] && ((new Date().getTime() - localCache.data[url]._) < localCache.timeout);
    },
    get: function (url) {
        console.log('Getting in cache for url' + url);
        return localCache.data[url].data;
    },
    set: function (url, cachedData, callback) {
        localCache.remove(url);
        localCache.data[url] = {
            _: new Date().getTime(),
            data: cachedData
        };
        if ($.isFunction(callback)) callback(cachedData);
    }
};

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
    if (options.cache) {
        //Here is our identifier for the cache. Maybe have a better, safer ID (it depends on the object string representation here) ?
        // on $.ajax call we could also set an ID in originalOptions
        var id = originalOptions.url+ JSON.stringify(originalOptions.data);
        options.cache = false;
        options.beforeSend = function () {
            if (!localCache.exist(id)) {
                jqXHR.promise().done(function (data, textStatus) {
                    localCache.set(id, data);
                });
            }
            return true;
        };

    }
});

$.ajaxTransport("+*", function (options, originalOptions, jqXHR, headers, completeCallback) {

    //same here, careful because options.url has already been through jQuery processing
    var id = originalOptions.url+ JSON.stringify(originalOptions.data);

    options.cache = false;

    if (localCache.exist(id)) {
        return {
            send: function (headers, completeCallback) {
                completeCallback(200, "OK", localCache.get(id));
            },
            abort: function () {
                /* abort code, nothing needed here I guess... */
            }
        };
    }
});

$(function () {
    var url = '/echo/jsonp/';
    $('#ajaxButton').click(function (e) {
        $.ajax({
            url: url,
            data: {
                test: 'value'
            },
            cache: true
        }).done(function (data, status, jq) {
            console.debug({
                data: data,
                status: status,
                jqXHR: jq
            });
        });
    });
});

Fiddle ICI Quelques problèmes, notre identifiant de cache dépend de la représentation de l'objet JSON lib json2.

Utilisez la vue Console (F12) ou FireBug pour afficher certains journaux générés par le cache.

TecHunter
la source
y a-t-il une raison pour laquelle vous mettez un rappel sur la localCache.setfonction? Pourquoi pas simplement doSomehing(jqXHR)après avoir configuré le cache?
cammil
Je préfère juste ça de cette façon donc je n'ai pas à faire quelque chose comme doSomething(localCache.set(url,jqXHR));mais c'est juste une préférence personnelle
TecHunter
2
Une suggestion pour améliorer cela pour soutenir l'utilisation de $ .ajax comme promesse? Renvoyer false de beforeSend annule la requête (comme il se doit) donc $ .ajax (...). Done (function (response) {...}). Fail (...) cesse de fonctionner maintenant parce que fail est invoqué plutôt que fait ... et je préfère ne pas les réécrire tous :(
franck102
2
@TecHunter Merci beaucoup pour cela. Trois autres améliorations légères pourraient être apportées. Premièrement, si plusieurs demandes sont adressées à la même ressource en même temps, elles manqueront toutes le cache. Pour résoudre ce problème, vous pouvez définir le cache d'un certain «id» sur en attente avec la première requête et différer l'envoi des résultats pour les requêtes suivantes jusqu'à ce que la première revienne. Deuxièmement, vous souhaiterez peut-être mettre en cache le résultat d'erreur d'une demande afin que toutes les demandes pour la même ressource obtiennent le même résultat.
mjhm
2
@TecHunter - Belle solution, j'ai utilisé cette solution avec un changement important. Si l'objet mis en cache est en cours de modification dans d'autres fonctions, cela causera un problème, donc lors de la configuration et de l'obtention de l'objet mis en cache, je retourne une copie de cet objet qui, comme indiqué ci-dessous: localCache.data[url] = { _: new Date().getTime(), data: _.cloneDeep(cachedData, true) }; _.cloneDeep(localCache.data[url].data, true)
Bharat Patil
12

Je cherchais la mise en cache pour le stockage de mon application phonegap et j'ai trouvé la réponse de @TecHunter, ce qui est génial mais fini localCache.

J'ai trouvé et j'ai appris que localStorage est une autre alternative pour mettre en cache les données renvoyées par un appel ajax. J'ai donc créé une démo en utilisant localStoragece qui aidera les autres qui voudront peut-être utiliser localStorageau lieu de localCachepour la mise en cache.

Appel Ajax:

$.ajax({
    type: "POST",
    dataType: 'json',
    contentType: "application/json; charset=utf-8",
    url: url,
    data: '{"Id":"' + Id + '"}',
    cache: true, //It must "true" if you want to cache else "false"
    //async: false,
    success: function (data) {
        var resData = JSON.parse(data);
        var Info = resData.Info;
        if (Info) {
            customerName = Info.FirstName;
        }
    },
    error: function (xhr, textStatus, error) {
        alert("Error Happened!");
    }
});

Pour stocker des données dans localStorage:

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
if (options.cache) {
    var success = originalOptions.success || $.noop,
        url = originalOptions.url;

    options.cache = false; //remove jQuery cache as we have our own localStorage
    options.beforeSend = function () {
        if (localStorage.getItem(url)) {
            success(localStorage.getItem(url));
            return false;
        }
        return true;
    };
    options.success = function (data, textStatus) {
        var responseData = JSON.stringify(data.responseJSON);
        localStorage.setItem(url, responseData);
        if ($.isFunction(success)) success(responseJSON); //call back to original ajax call
    };
}
});

Si vous souhaitez supprimer localStorage, utilisez l'instruction suivante où vous le souhaitez:

localStorage.removeItem("Info");

J'espère que cela aide les autres!

immayankmodi
la source
Salut, à localStorage.removeItem("Info");propos de "info"l'URL?
vsogrimen
@vsogrimen infoest l'objet pour stocker les données dans localStorage.
immayankmodi
1
continuez à obtenir responseJSON is not defined. Aucun moyen de réparer cela? (mon type de données est html)
Nikita 웃
@CreativeMind, supprimez reponseJOSN et utilisez "var responseData = JSON.stringify (data);" au lieu de cela, faites de même avec succès (données)
Unicco
Je préfère le localStorage car j'avais besoin de cache sur plusieurs demandes.
KeitelDOG
9

Tous les navigateurs modernes vous fournissent des API de stockage. Vous pouvez les utiliser (localStorage ou sessionStorage) pour sauvegarder vos données.

Tout ce que vous avez à faire est, après avoir reçu la réponse, de la stocker dans le stockage du navigateur. Ensuite, la prochaine fois que vous trouverez le même appel, recherchez si la réponse est déjà enregistrée. Si oui, renvoyez la réponse à partir de là; sinon faire un nouvel appel.

Le plugin Smartjax fait également des choses similaires; mais comme votre exigence consiste simplement à enregistrer la réponse à l'appel, vous pouvez écrire votre code dans votre fonction jQuery ajax success pour enregistrer la réponse. Et avant de passer un appel, vérifiez simplement si la réponse est déjà enregistrée.

Paul Shan
la source
J'ai des réponses enregistrées dans IndexedDB, existe-t-il un moyen de vérifier IndexedDB? De plus, si j'utilise le stockage de session, existe-t-il un moyen de vérifier si la réponse est présente ou non en utilisant jQuery. Je ne peux inclure aucune bibliothèque autre que jQuery. Merci,
Abhishek Aggarwal
7

Si j'ai bien compris votre question, voici la solution:

    $.ajaxSetup({ cache: true});

et pour des appels spécifiques

 $.ajax({
        url: ...,
        type: "GET",
        cache: false,           
        ...
    });

Si vous voulez l'opposé (cache pour des appels spécifiques), vous pouvez définir false au début et true pour des appels spécifiques.

printemps
la source
2
c'est exactement le contraire
TecHunter
Cela fonctionne pour moi, merci! Il est étrange que la mise en cache n'ait pas été activée par défaut sur ma page.
Timur Nurlygayanov
2

Ancienne question, mais ma solution est un peu différente.

J'écrivais une application Web d'une seule page qui faisait constamment des appels ajax déclenchés par l'utilisateur, et pour le rendre encore plus difficile, il fallait des bibliothèques utilisant des méthodes autres que jquery (comme dojo, xhr natif, etc.). J'ai écrit un plugin pour l'une de mes propres bibliothèques afin de mettre en cache les requêtes ajax aussi efficacement que possible d'une manière qui fonctionnerait dans tous les principaux navigateurs, quelles que soient les bibliothèques utilisées pour faire l'appel ajax.

La solution utilise jSQL (écrit par moi - une implémentation SQL persistante côté client écrite en javascript qui utilise indexeddb et d'autres méthodes de stockage dom), et est fournie avec une autre bibliothèque appelée XHRCreep (écrite par moi) qui est une réécriture complète de l'objet XHR natif.

Pour implémenter, tout ce que vous devez faire est d'inclure le plugin dans votre page, qui se trouve ici .

Il existe deux options:

jSQL.xhrCache.max_time = 60;

Définissez l'âge maximum en minutes. toutes les réponses mises en cache plus anciennes que cela sont à nouveau demandées. La valeur par défaut est 1 heure.

jSQL.xhrCache.logging = true;

Lorsqu'il est défini sur true, les faux appels XHR seront affichés dans la console pour le débogage.

Vous pouvez vider le cache sur une page donnée via

jSQL.tables = {}; jSQL.persist();
Une fois je me suis battu contre un ours.
la source
Je ne trouve pas votre plugin sur github et sur le site officiel.Veuillez mettre à jour votre réponse J'ai besoin de votre plugin :) Je suis votre adepte dans github. Bon travail;) @ occams-razor
Alican Kablan
-1
        function getDatas() {
            let cacheKey = 'memories';

            if (cacheKey in localStorage) {
                let datas = JSON.parse(localStorage.getItem(cacheKey));

                // if expired
                if (datas['expires'] < Date.now()) {
                    localStorage.removeItem(cacheKey);

                    getDatas()
                } else {
                    setDatas(datas);
                }
            } else {
                $.ajax({
                    "dataType": "json",
                    "success": function(datas, textStatus, jqXHR) {
                        let today = new Date();

                        datas['expires'] = today.setDate(today.getDate() + 7) // expires in next 7 days

                        setDatas(datas);

                        localStorage.setItem(cacheKey, JSON.stringify(datas));
                    },
                    "url": "http://localhost/phunsanit/snippets/PHP/json.json_encode.php",
                });
            }
        }

        function setDatas(datas) {
            // display json as text
            $('#datasA').text(JSON.stringify(datas));

            // your code here
           ....

        }

        // call
        getDatas();

entrez la description du lien ici

user1634982
la source