What are possible techniques to cache an Ajax response in Javascript? [closed] - javascript

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
I am implementing a Javascript module manager that loads javascript files via XHR object. The problem of this method is resources caching:
Firstly, XHR rely on in-built browser caching mechanism which is OK but it's behaviour depends on the browser implementation.
Also there is a localStorage and there is a basket.js which uses localStorage to cache downloaded scripts, the problem is in limited size of storage which is usually 5-10MB. Besides, localStorage is a shared place for many scripts which also use it to store data.
And there is a Cache interface of the ServiceWorker API, but it is available only in ServiceWorker runtime so it doubtingly fit my needs.
Do anyone know some smart old or new javascript caching technique he's using in his project, or maybe heard of?
Note: Please, don't propose to use jQuery .ajax which is an interface to XHR, or any other library that implements an interface to in-built Javascript features.
Edit: There have been some valuable proposes:
Use library called localForage. The library represents a unified API to IndexedDB, WebSQL and localStorage, which one is used depends on browser.
Use IndexedDB which is truly powerfull storage with no significant space limits. The only concern is that only modern browsers implement IndexedDB.

This is specific for JQUERY....
Your can make ajax set up as cached.
$.ajaxSetup({ cache: true});
and if for specific calls you don't want to make cached response then call
$.ajax({
url: ...,
type: "GET",
cache: false,
...
});
If you want opposite (cache for specific calls) you can set false at the beginning and true for specific calls
If you want to store the result of ajax response, you can make use of Local Storage. All the modern browsers provides you storage apis. You can use them (localStorage or sessionStorage) to save your data.
All you have to do is after receiving the response store it to browser storage. Then next time you find the same call, search if the response is saved already. If yes, return the response from there; if not make a fresh call.
Smartjax plugin also does similar things; but as your requirement is just saving the call response, you can write your code inside your jQuery ajax success function to save the response. And before making call just check if the response is already saved.

Since indexeddb is a method used for storing data client-side, allows indexed database queries.
And this are the supported browsers
http://caniuse.com/#feat=indexeddb
And this are the only issues
Firefox (prior to version 37) and Safari do not support IndexedDB inside web workers.
Not supported in Chrome for iOS or other iOS WebViews.
Chrome 36 and below did not support Blob objects as indexedDB values.
Here is another similar polyfill you can try, but in my (albeit limited) experience, both polyfills are buggy/incomplete. They both also have many open issues on GitHub of people reporting problems. And when I tested one of them (I forget which one) it was significantly slower than native IndexedDB.
Maybe it's possible to create a decent polyfill, but the current ones don't seem to be doing the job.
Should I use WebSQL which was deprecated?
The problem with WebSQL is that it's never going to be supported in IE or Firefox. You could probably get away with WebSQL if you're only targeting mobile browsers, at least until Firefox OS or Windows Phone grabs significant market share.
Are there any plans to support IndexedDB in the future for all the non-supported browsers?
Let's be clear. You're asking about Apple, since everyone else supports IndexedDB in their latest browser (iOS Chrome uses Apple's rendering engine because Apple won't let them do anything else).
Not only does Apple not support IndexedDB, they haven't publicly said anything about it (as far as I can tell... and I have done a fair amount of searching). Which seems pretty weird. So as best I can tell, nobody has any idea if Apple ever plans to support IndexedDB. The conspiracy theorist in me thinks maybe they're trying to sabotage HTML5 apps to force people to write native apps, but that's purely speculation.
In total, this leaves us developers in a pretty shitty situation. There is no good cross-platform solution. I recommend you complain to Apple about it. That's what I've done, and I've asked my users who want to use my IndexedDB-based app on iOS to do the same. Still no word from Apple.
UPDATE - Indexeddb is now supported in iOS 8 as stated in WWDC 2014 - but unfortunately it's broken pretty badly.
Considerin also that Subresource Integrity -
Subresource Integrity enables browsers to verify that file is delivered without unexpected manipulation.
Does not have knowissues? so far ?
The i will suggest that you can go with
Subresource based solution if mobile is your main target
indexeddb if mobile is not your main target and use of the publicly available implementations for mobile
If all of the above sound too complex for you then
var localCache = {
data: {},
remove: function (url) {
delete localCache.data[url];
},
//a cached version exists
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); //log only!
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);
},
timeout: 600, //in seconds
};
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
if (options.cache) {
var complete = originalOptions.complete || $.noop,
url = originalOptions.url;
//remove jQuery cache as you have your 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 = 'your url goes here';
$('#ajaxButton').click(function (e) {
$.ajax({
url: url,
data: {
test: 'value'
},
cache: true,
complete: doSomething
});
});
});
//ToDo after ajax call finishes, or cached version retrived
function doSomething(data) {
console.log(data);
}

Another specific JQUERY answer ?
Not sure it answers your question but that could help. It s caching ajax calls with a timeout.
In the prefiltering, list the various PHP ajax calls you want to add for caching.
In this example, cache is enabled with a 10 minutes timeout.
/*----------------------------*/
/* set ajax caching variables */
/*----------------------------*/
$.set_Ajax_Cache_filters = function () {
var localCache = {
timeout: 600000, // 10 minutes
data: {}, //#type {{_: number, 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) {
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);
}
};
/*----------------------*/
/* set ajax pre filters */
/*----------------------*/
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
// list of allowed url to cache
if (url !== '..............file.php') {
return false;
}
if (options.cache) {
var complete = originalOptions.complete || $.noop,
url = originalOptions.url;
options.cache = false;//remove jQuery cache using proprietary one
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);
};
}
});
};

To do this, the best technic is using local cache, ajaxPrefilter and ajax cache option, the combination of these three will create you the solid cache you want, that you can easily control
Here is a code example:
var localCache = {
data: {},
remove: function (url) {
delete localCache.data[url];
},
//a cached version exists
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); //log only!
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);
},
timeout: 600, //in seconds
};
$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
if (options.cache) {
var complete = originalOptions.complete || $.noop,
url = originalOptions.url;
//remove jQuery cache as you have your 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 = 'your url goes here';
$('#ajaxButton').click(function (e) {
$.ajax({
url: url,
data: {
test: 'value'
},
cache: true,
complete: doSomething
});
});
});
//ToDo after ajax call finishes, or cached version retrived
function doSomething(data) {
console.log(data);
}
Enjoy your coding

Try this might work
var cache = {};
var formatTweets(info) {
//formats tweets, does whatever you want with the tweet information
};
//event
$('myForm').addEvent('submit',function() {
var handle = $('handle').value; //davidwalshblog, for example
var cacheHandle = handle.toLowerCase();
if(cache[cacheHandle] != "undefined") {
formatTweets(cache[cacheHandle]);
}
else {
//gitter
var myTwitterGitter = new TwitterGitter(handle,{
count: 10,
onComplete: function(tweets,user) {
cache[cacheHandle] = tweets;
formatTweets(tweets);
}
}).retrieve();
}
});

Related

ASP.NET Core - Client-side AJAX not working for IE11/Edge

I've literally been researching for 5+ days for a problem that only exists within IE and Edge (works in Chrome, FireFox, etc). I've gone through pretty much all of the links and tried nearly everything, but it seems that a lot of the content/answers out there from older posts are very outdated.
Background: I created an ASP.NET Core web service - it is running on some port on localhost. Ideally, I'd have my users open an locally-residing html document (meaning living on their local filesystem and not hosted on a server) that pings the web service through AJAX (that returns some JSON) - allowing for that content from the web service to be displayed in the html document. It literally works in all browsers except IE and Edge (I'm using IE11).
Here is my Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
...
services.AddCors(o => o.AddPolicy("ContentServices", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
services.AddMvc();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseCors("ContentServices");
...
}
Here is my SearchController.cs:
[EnableCors("ContentServices")]
public SearchResponse Query(string index, string input, SearchType searchType, int page = 0, int pageSize = 20)
{
SearchResponse sr = new SearchResponse;
...
return sr;
}
Here is my JavaScript code:
function WebService(url, successMsg, errorMsg) {
return $.ajax({
url: searchDestinationUrl + url,
type: "GET",
crossDomain: true,
dataType: "json",
success: function (results) {
return results;
},
error: function (xhr, status, error) {
console.error(error + " - " + errorMsg);
}
});
}
function RunGeneralSearch(input, options) {
var search = WebService("search/query?index=" + index + "&input=" + input + "&page=" + options.content.pageIndex + "&pageSize=" + options.content.pageSize, "", "Search failed.")
.then(function (response) {
var searchResults = [];
searchResults.content = [];
searchResults.contentTotal = response.total;
searchResults.clientPaging = false;
var results = response.results;
if (results.length > 0) {
var Search = MadCap.CreateNamespace("WebHelp.Search");
for (var i = 0; i < results.length; i++) {
var result = results[i];
var rank = result["rank"];
var title = result["title"];
var link = result["link"];
var linkUrl = GetLinkUrl(link);
var abstractText = result["abstractText"];
var searchResult = new Search.SearchResult(rank, title, linkUrl.FullPath, unescape(abstractText));
searchResults.content.push(searchResult);
}
}
return searchResults;
});
return search;
};
}
Things I've tried:
Using $.support.cors = true will work for IE with the exception
that it will prompt an warning for every ping that is made to the
web service regarding security: "This page is accessing information that is not under its control. This poses a security risk. Do you want to continue?" This is absolutely annoying, and I will not have my customers modify their security settings for this. This also does not work for Edge at all.
Changing the crossDomain attribute in the AJAX call to false - behavior is the same as the above except that it will not work for all other browsers now.
I've read a lot about using the dataType: 'jsonp' - but as far as I know, it's deprecated and only supports GET requests. So I did not try this and want to stay away from it.
I've also read that because I'm trying to access the web service through a locally-residing unhosted file (i.e. url is file:// instead of http://) that it is technically not supported as an origin - so allowing any origin wouldn't help in this case. I'm not sure how true this is or even how to test this. Regardless, I have not tried serving the html file over localhost because I do not want to require my clients to have to do this just for IE/Edge when it works completely fine with all other browsers.
I've tried using an XMLHttpRequest instead of and AJAX call. It requires me to then do JSON.parse(result); but seems to work perfectly for all browsers including IE except Edge...
I understand to some extent about CORS and the limitations of the same origin policy, but I absolutely don't understand what the difference is between Microsoft browsers and the rest.
Please help!

Prevent duplicate $.ajax calls using result cache

A similar question has been asked before, but I don't believe it overcomes the challenges in this case because my function calls are all together, so please bear with me (I'll delete the question if appropriate).
I have a number of dashboard widgets that each make an $.ajax call, receive a JSON result and then process that to render a Google chart. The widgets can be used multiple times, so there are some duplicated AJAX calls occurring, e.g.
RenderChart('/api/LoginCount?DaysPrevious=7', 'ColumnChart'); // some parameters removed, for brevity
RenderChart('/api/LoginCount?DaysPrevious=7', 'AreaChart');
RenderChart('/api/LoginCount?DaysPrevious=7', 'Table');
The problem is that this generates multiple calls to the same URL, which is extremely wasteful. I saw in the linked question that an object can be used to cache the results, but when I applied this, it didn't seem to work because the second call to RenderChart (immediately after the first) saw there was no data (yet) in the cache, and called the URL again.
My code is:
function LoadDataFromApi(apiUrl) {
return $.ajax({
type: 'GET',
url: apiUrl,
dataType: "json",
success: function (data) { }
});
}
function RenderChart(apiUrl, chartElementId, chartType, chartOptions) {
$.when(LoadDataFromApi(apiUrl)).done(function (data) {
var el = $('#' + chartElementId);
try {
var arrayOfArrays = BuildGoogleArrayFromData(data); // Transform JSON into array of arrays (required by Google Visualization)
$(el).empty();
if (arrayOfArrays.length == 0) { // Data found?
$(el).append('<p class="noData">No data was found.</p>');
} else {
var wrapper = new google.visualization.ChartWrapper({ // alert(wrapper.getChartType()); // wrapper is the only way to get chart type
chartType: chartType,
dataTable: google.visualization.arrayToDataTable(arrayOfArrays, false),
options: chartOptions,
containerId: chartElementId
});
wrapper.draw();
}
}
catch (ex) {
$(el).append('<p class="error">An error occurred: ' + ex.message + '</p>');
}
});
}
Ideally it would be good to cache the arrayOfArrays value, as at this point all additional processing is also complete. However, getting JavaScript to see what other API calls are in progress, and wait for them is where I'm struggling. Is this possible to achieve?
If anyone can handhold me into achieving both I'll put a bonus on the question. I read about promises, but I need to support IE9+.
I can think of making a cache map with URL as its key, and the AJAX request as its value. We can change your LoadDataFromApi function to leverage this cache, and return appropriate AJAX request, if exists, else make a new request.
Following is a snippet of how it can be done.
var requestCache = {};
function LoadDataFromApi(apiUrl) {
if (!requestCache[apiUrl]) {
requestCache[apiUrl] = $.ajax({
type: 'GET',
url: apiUrl,
dataType: "json"
});
}
return requestCache[apiUrl];
}
This way, you can call LoadDataFromApi without any limit, and chain your promise handlers like this:
LoadDataFromApi('http://fake.url')
.then(function(data) {
// use the data in one widget
})
LoadDataFromApi('http://fake.url')
.then(function(data) {
// use this data in another widget
})
// ... and so on
This way the AJAX call for a particular URL will be made only once, and the result will be shared among the promise handlers.

Multiple ajax calls fired simultaneously not working properly

I created a site which load every few seconds data from multiple sources via AJAX. However I experience some strange behavior. Here is the code:
function worker1() {
var currentUrl = 'aaa.php?var=1';
$.ajax({
cache: false,
url: currentUrl,
success: function(data) {
alert(data)
},
complete: function() {
setTimeout(worker1, 2000);
}
});
}
function worker2() {
var currentUrl = 'aaa.php?var=2';
$.ajax({
cache: false,
url: currentUrl,
success: function(data) {
alert(data)
},
complete: function() {
setTimeout(worker2, 2000);
}
});
}
The problem is that many times, one of the workers returns NaN. If I change the frequency of calls for, lets say, 2000 and 1900, then everything is working ok and I got almost no NaN results. When those frequencies are same, I get over 80% NaN results for one of the calls. It seems like the browser cannot handle two requests called at exact same time. I use only those two workers, so the browser shouldn't be overloaded by AJAX requests. Where is the problem?
Note that the aaa.php works with the mySql database and do some simple queries base on parameters in url.
All you need is $.each and the two parameter form of $.ajax
var urls = ['/url/one','/url/two', ....];
$.each(urls, function(i,u){
$.ajax(u,
{ type: 'POST',
data: {
answer_service: answer,
expertise_service: expertise,
email_service: email,
},
success: function (data) {
$(".anydivclass").text(data);
}
}
);
});
Note: The messages generated by the success callback will overwrite
each other as shown. You'll probably want to use
$('#divid').append() or similar in the success function.
Maybe, don't use these workers and use promises instead like below? Can't say anything about the errors being returned though without looking at the server code. Below is working code for what it looks like you are trying to do.
This is a simple example but you could use different resolvers for each url with an object ({url:resolverFunc}) and then iterate using Object.keys.
var urls = [
'http://jsonplaceholder.typicode.com/users/1',
'http://jsonplaceholder.typicode.com/users/2',
'http://jsonplaceholder.typicode.com/users/3',
'http://jsonplaceholder.typicode.com/users/4',
'http://jsonplaceholder.typicode.com/users/5',
'http://jsonplaceholder.typicode.com/users/6',
'http://jsonplaceholder.typicode.com/users/7'
]
function multiGet(arr) {
var promises = [];
for (var i = 0, len = arr.length; i < len; i++) {
promises.push($.get(arr[i])
.then(function(res) {
// Do something with each response
console.log(res);
})
);
}
return $.when(promises);
}
multiGet(urls);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

How to store the ajax response in browser cache with jquery

I'm trying to store the ajax response in browser cache. I know to store it for only url like (example.com), but want to store the data which having url like (example.com?xyz=abc%20efg&mno=hjk).
My code :
beforeSend: function () {
var addC=url; // I want to add data also with this url
console.log(addC);
if (localCache.exist(addC)) {
printRes(localCache.get(url));
console.log("cached values");
return false;
}
return true;
},
And
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);
}
};
Please anyone help me to solve this.
By reading the docs from
MDN i Think you are doing wrong. If you want to cache ajax response CACHE property to true
in your ajax call ,also see to it that you provide proper cache expiration header from server.
Also
[from MDN]
Never access cached files by using traditional GET parameters (like other-cached-
page.html?parameterName=value). This will make the browser bypass the cache and attempt to get
it from network. To link to cached resources that have parameters parsed in JavaScript use
parameters in the hash part of the link, such as other-cached-page.html#whatever?parameterName=value.
if you are looking for local storage[key value storage]
globalStorage['cache_1'].setItem("key",data);

Why does AJAX json script return extra 0 (zero)

I have an AJAX function in WordPress that calls a PHP function to return the value of a transient record in the Database.
When I call the function using jQuery, I receive the result however it always has an extra 0 (zero) appended to the value.
Here is my jQuery function:
(function($) {
$(document).ready( function() {
var AdvancedDashboardWidget = function(element, options)
{
var ele = $(element);
var settings = $.extend({
action: '',
service: '',
countof: '',
query: '',
callback:''
}, options || {});
this.count=0;
var url='';
switch(settings.service)
{
case 'facebook':
if(settings.countof=='likes' || settings.countof=='talks')
{
ajaxCall(action,ele,settings);
}
}
};
var ajaxCall = function(action,ele,settings){
opts = {
url: ajaxurl, // ajaxurl is defined by WordPress and points to /wp-admin/admin-ajax.php
type: 'POST',
async: true,
cache: false,
dataType: 'json',
data:{
action: settings.action // Tell WordPress how to handle this ajax request
},
success:function(response) {
//alert(response);
ele.html(response);
return;
},
error: function(xhr,textStatus,e) { // This can be expanded to provide more information
alert(e);
//alert('There was an error');
return;
}
};
$.ajax(opts);
};
$.fn.advanceddashboardwidget = function(options)
{
return this.each(function()
{
var element = $(this);
// Return early if this element already has a plugin instance
if (element.data('advanceddashboardwidget')) return;
// pass options to plugin constructor
var advanceddashboardwidget = new AdvancedDashboardWidget(this, options);
// Store plugin object in this element's data
element.data('advanceddashboardwidget', advanceddashboardwidget);
});
};
});
})(jQuery);
There are more helper functions involved however this is the main jQuery function that communicates with WordPress and returns the value of the PHP function.
The issue is that if the value is returned as "99" for example it will be returned as "990"
Here is the PHP function that jQuery is calling:
/**
* Get Facebook Likes
*/
public function get_facebook_likes(){
echo 99;
}
If I change the above to return 99; I receive plain 0
Your function should use wp_send_json to encode the PHP as JSON and sent it back to the AJAX request handler. This will also stop executing of any following PHP too, so there is no need to use exit or die.
So for your specific example, you would use:
/**
* Get Facebook Likes
*/
public function get_facebook_likes(){
wp_send_json(99);
}
This is old question but I'm going to answer this. wp_send_json() function may help but not always. There could be some moments when you can't use this function. Like, when you load posts by ajax, you are getting some template part, you can use the function. That's why WordPress documentation suggests to make use of the die() function.
So in the end, your php function should look like this:
/**
* Get Facebook Likes
*/
public function get_facebook_likes() {
echo 99;
die();
}
Use Firebug and view the actual net data transmitted and received. Determine if the error is coming from the javascript side or the PHP side. Copy the net request and paste it into a separate browser window to see the raw result. If it is PHP, pursue that. if it is the javascript doing something, let us know.

Categories

Resources