Managing cache synchronously in js - javascript

I'm maintaining a cache in JavaScript using jquery in a global cache variable.
Whenever new information is received using AJAX it is added to the cache.
If it is not in the cache, I want to AJAX it from the server.
I want to implement a function to query on demand and to use it like so:
$("#label").html(GetName("user123"));
Where GetName() should be like:
function GetName(username) {
if (Cache[username] != null) return Cache[username];
else
return QueryUsernameFromServer(username)
}
QueryUsernameFromServer() should be like:
function QueryUsernameFromServer (username) {
return $.ajax(…);
}
However, $.ajax is async meaning that it cannot wait for a value (and thus cannot return it).
Using $.ajax in sync mode is highly not recommended (browser hangs and it doesn’t support JSONP), http://api.jquery.com/jQuery.ajax/
Using this, http://www.techfounder.net/2008/05/17/simple-javascript-cache/, approach requires a callback. However, creating a callback for each use is not desired.
Is there a good way to implement a “cache on demand” function in js and ajax without a dedicated callback for each use?

You'll need to have the callback, because AJAX is...wait for it..."A"synchronous.
Just add a callback to your functions that query the cache. It's pretty simple:
function getName(username, callback){
if(cache[username]){
// cache hit, immediately invoke the callback
callback(cache[username]);
}else{
// assumes this query function updates the cache and invokes the
// 2nd parameter when it completes
queryUsernameFromServer(username, function(){
// invoke the callback now
callback(cache[username]);
});
}
}
And simply convert to an async style in your code:
Before:
var name = getName('jvenema');
After:
getName('jvenema', function(name){
});

If your using jQuery 1.5+ then you can just use deferreds.
function GetName(username) {
if (Cache[username] != null) return $.when(Cache[username]);
else
return QueryUsernameFromServer(username)
}
And use deferreds as follows
GetName('jvenema').done(function(name) {
});

Related

ajax interface without then

I want to package a ajax call into an interface without then.
If i do like this, it will just return 'No ajax return';
var ajaxReturn = ajaxFunction();
function ajaxFunction(){
var text = 'No ajax return';
// get fileName using an ajax get
$.ajax();
return text;
}
If i do like this, it will be ugly for using then;
function ajaxFunction(){
var text = 'No ajax';
var dtd = $.Deferred();
$.ajax();
return dtd.promise();
}
$.when(ajaxFunction()).then();
I just want the interface to be simple and return the right thing, can i?
//return the right
var ajaxReturn = ajaxFunction();
function ajaxFunction(){
var text = 'No ajax';
var dtd = $.Deferred();
$.ajax();
return dtd.promise();
}
$.when(ajaxFunction()).then();
Whoa, what is all that? You do need .then but you don't need most of the surrounding stuff. $.ajax generates a promise for you. You don't need to make a promise object yourself. In fact, often the only reason you need to manually set up a Deferred/Promise directly is if you're using some library that sets up callbacks and doesn't use promises itself.
function ajaxFunction(){
return $.ajax();
}
ajaxFunction().then(function(data) { ... });
Now, let's say that you didn't actually want to return the JSON structure on the end of the ajax function; you want to take out just a number from inside of it, or tweak one value to make it an easier-to-use function for its callers. Easy enough:
function ajaxFunction(){
return $.ajax().then(function(data) {
return data[12].number;
}
}
ajaxFunction().then(function(number) { ... });
In direct answer to your question: No, what you asked for isn't possible. Whenever your JavaScript methods are running, the browser can't process other events like clicks and even basic scroll operations. So, any long-running operations (like contacting the server) do not return straight away, and instead offer a callback operation.
Well..., ajax is asynchronous so you either use .then() or use a callback logic... Doing synchronous ajax is not a option for me, so I won't even mention it.
The alternative to .then() would be something like this:
ajaxFunction(function(res){ // pass a function into it
// this will be called when the ajax is done
alert(res);
});
function ajaxFunction(callback){
// get fileName using an ajax get
$.ajax({
success: callback
});
}
But again, maybe you can use just a normal ajax callback pattern anyway
$.ajax({
...
success: function(res){
// use the res
}
});
Ajax is asynchronous. then is designed to make writing async operations look more similar to synchronous code and can actually be very elegant.
Additionally, $.ajax() returns a promise and is well suited to be written as follows:
function ajaxFunction(){
return $.ajax();
}
ajaxFunction().then(function(response){
// do whatever you want with the response
})
You simply can't write asynchronous code that way (ajaxResult = ajaxFunction()). The interpreter is going to keep trucking along line by line and ajaxResult will not be ready in time.
Read up on chaining $.Deferred's. It will really clean up your async code.

Calling JavaScript functions. First function waits for the other which intern having the one more function for confirmation

I have two JavaScript function in two different files. On click I am calling first function to delete and second function for refresh the text in the div. The problem is delete function is having confirm action. So if I call one after the other refresh function is executing immediately. But I want refresh function to execute after confirmation (Note:delete and refresh JavaScript functions are there in two different projects)
Below is the sample:
function deleteIFAsset(a) {
var id1 = a.id;
IframeCloudEditor.deleteFileFromTimeline(id1);
refreshAsseListt();
}
You'll have to use a callback. Any properly-designed library with asynchronous operations (like waiting for the user to confirm an action in an event-driven environment like a browser) should offer a callback for when that operation is complete. You haven't said what library the deleteFileFromTimeline comes from, but hopefully it offers a callback. The callback may be an argument you pass, or it may be part of a "promise" API.
If it accepts a callback directly, that would look something like this:
function deleteIFAsset(a) {
var id1 = a.id;
IframeCloudEditor.deleteFileFromTimeline(id1, function() {
refreshAsseListt();
});
}
or
function deleteIFAsset(a) {
var id1 = a.id;
IframeCloudEditor.deleteFileFromTimeline(id1, refreshAsseListt);
}
...if your refreshAsseListt (was that supposed to be refreshAssetList?) is compatible with what the library does with the callback (the arguments it passes to it and what it does with the return value).
If it returns a promise instead, that would look something like this:
function deleteIFAsset(a) {
var id1 = a.id;
IframeCloudEditor.deleteFileFromTimeline(id1).then(refreshAsseListt);
}
("Promises" are also sometimes called "futures" or "deferred objects.")
if you can change the code of deleteFileFromTimeLine, you can change it to return the result of the confirmation.
and execute refreshAsseListt.
example
function deleteFileFromTimeLine(ID)
{
...
return Confirm('Delete File?');
}
and change your code like this
function deleteIFAsset(a) {
var id1 = a.id;
if(IframeCloudEditor.deleteFileFromTimeline(id1))
{
refreshAsseListt();
}
}
You are searching for a solution, to execute your javascript synchronous. But javascript is always executed synchronously except for special cases like ajax requests or file access.
My advice is to use a callback function to solve this problem.

How to ensure that my amplify request is completed before returning a value?

In one of my files, I make a call as follows:
var jobsString = getDropdownJobs();
It calls this function :
function getDropdownJobs() {
var jobsString = amplify.store("JobsList");
if (typeof jobsString === 'undefined') {
// Amplify sends a request for getJobs, if it exists in cache, it will return that value.
// If it does not exist in cache, it will make the AJAX request.
amplify.request("getJobs", function (data) {
// Leave a blank option for the default of no selection.
var jobsString = '<option value=""></option>';
// Append each of the options to the jobsString.
$.each(data.jobs, function () {
jobsString += "<option " + "value=" + this.JobNo_ + ">" + this.JobNo_ + " : " + this.Description + this.Description2 + "</option>";
});
// Store the jobsString to be used later.
amplify.store("JobsList", jobsString);
});
}
return jobsString;
}
Where the amplify definition of "GetJobs" is :
amplify.request.define("getJobs", "ajax", {
url: "../api/Job/Jobs",
dataType: "json",
type: "GET",
cache: "persist"
});
Whenever it returns, it's undefined. I put "async: false" in the AJAX definition and it didn't change anything.
How can I make sure that the value is there before returning?
I'm not familiar with Amplify, but its API says
Requests made through amplify.request will always be resolved asynchronously
So, you'll have to pass a callback into getDropdownJobs, to be executed after jobsString is filled, and any code that relies on the value goes in it.
Alternatively you could use Amplify's pub/sub system to subscribe an event for when jobsString is filled and publish to it during getDropdownJobs.
Sorry if I'm misunderstanding your question, but every time you do something like this, an asynchronous requests, you need to wait for the answer. Before I suggest an alternative some quick tips I tend to follow:
Usually methods like your getJobsDropdown don't return anything, or potentially they can return promises. Take a look at jquery promises or this article for more advanced material on chaining those same promises.
The most trivial thing you can use is a callback, some method on the context on the invoker of getJobsDropdown where you want to process the data. This will allow you to resume your program when the data is ready.
Try this:
function getDropdownJobs(callback) {
// some code (...)
amplify.request("getJobs", function (data) {
// your processing and when done
callback();
});
}
Potentially you can pass the data in the callback. A usual call of the getDropdownJobs would be:
function processResults() { // This is your callback }
function getData() {
// This is where you call getDropDownJobs
getDropDownJobs(processResults);
}
Was this helpful? I hope so.
Cheers.
If you are using jQuery and do not mind coupling amplify with the jQuery framework you can use this plugin, allowing you to utilise jQuery deferred objects:
amplify-request-deferred plugin
Here is an article with some background information and an overview of the plugin usage:
Adding jQuery Deferred Support to AmplifyJS Request

Why is return not working in getJSON and why cant I write into variable from getJSON?

I have a function which uses getJSON but its not working like I expected.
function balbla(name, param) {
$.getJSON("/blabla.json?name=" + name + "&param=" + param, function(data) {
return data.bla;
});
}
When I use alert(data.bla) in the getJSON method it works but when I try return data.bla it doesnt. Also when I create a variable and try to write the value of data.bla to it it simply doesnt work!
// I tried this:
function getRouteData(name, param) {
return $.getJSON('/routes_js.json', {route:name, opt: param});
}
function getRoute(name, param) {
getRouteData(name, param).done(function(data) {
return data.route;
});
}
But when I call getRoute("bla", "blub") it still returns undefined.
AJAX is asynchronous. You cannot easily return a value in such a function that depends on the result of the AJAX call. Change your function to accept a callback:
function balbla(name, param, cb) {
$.getJSON('/blabla.json', {name:name, param: param}, function(data) {
cb(data.bla);
});
}
And use it like this:
balbla('foo', 'bar', function(bla) {
// do stuff
});
An even cleaner way would be returning the jqXHR object:
function balbla(name, param) {
return $.getJSON('/blabla.json', {name:name, param: param});
}
When calling it, use the deferred/promise interface of the jqXHR object to attach a success callback:
balbla('foo', 'bar').done(function(data) {
alert(data.bla);
});
Note that using $.ajax() in synchronous mode is not an option you should consider at all. It may hang the browser's UI (or at least the active tab) until the request finished. Besides that, asynchronous callbacks are the way everyone does it.
If you do not like using callback functions, you could use a preprocessor such as tamejs to generate the asynchronous functions automatically.
The function with your return statement:
function(data) {
return data.bla;
}
… is not being called by your code (it is being called deep inside jQuery), so you have no way to put an assignment of the left hand side of the function call.
It is also being called as part of an asynchronous function, so the balbla function will have finished running and returned before it the anonymous one is ever called.
If you want to do something with the response data, do it inside the anonymous callback function.
getJSON is asynchronous, not synchronous. You need to use a callback so your logic needs to be done in two steps. Calling step and the processing step.

How do you dynamically call a function in Actionscript 3 from Javascript at runtime without using eval()?

I'm trying to build an API in JS that will perform some operations and then execute the callback that's registered in AS when it's done. Because it's an API, I am just providing a JS method signature for another developer to call in Flash. Thus, the callback name that's registered in the AS part of the code should be a parameter that's passed in to the JS API in order for JS to communicate back to Flash.
For example:
[AS3 code]
ExternalInterface.addCallback("flashCallbackName", processRequest);
ExternalInterface.call("namespace.jsFnToCall", flashCallbackName);
function processRequest(data:String):void
{
//do stuff
}
[JS code]
var namespace =
{
jsFnToCall: function(callback)
{
//Do stuff in this function and then fire the callback when done.
//getFlashMovie is just a util function that grabs the
//Flash element via the DOM; assume "flash_id"'s a global var
//Below does not work...it's what I'd be ideally be doing some how.
getFlashMovie(flash_id).callback(data);
}
};
Because the definition of the function is in AS, I can't use the window[function name] approach. The only way I can think of is to build the callback in a string and then use the eval() to execute it.
Suggestions? T.I.A.
Well, I can think of one thing I would try, and one thing that would work.
What I would try first.
getFlashMovie(flash_id)['callback'](data);
What would work: Have the callback always be the same, say callback. The first parameter to the callback could be used to determine what actual function to call in flash. For example:
function callback($fn:String, $data:*) {
// either
this[$fn]($data);
// or
switch ($fn) {
case "callback1":
DoSomeCallback($data);
break;
}
Additionally passing the objectID makes it a bit simpler:
ExternalInterface.addCallback("flashCallbackName", processRequest);
ExternalInterface.call("namespace.jsFnToCall", ExternalInterface.objectID, "flashCallbackName");
Then in your JS:
var namespace =
{
jsFnToCall: function(objectID, callback)
{
//Do stuff in this function and then fire the callback when done.
document[objectID][callback](data);
}
};

Categories