Reputation: 60767
I have a client-side JS app that needs to load a template to display each item. Let's say they're notes.
The problem is in the asynchronous part. I can't make it so that the template is loaded only once. It loads every time a note is calling the render
function.
Here is some code:
var notes = [ {}, {}, {} ] // Some Note objects
notes.forEach( function( note ) {
render( note )
}
// Have some variable to hold the template
var template
// Now on the render function
function render( note ) {
// First, if the template exists, go straight to the next function
if ( template ) {
// The display function takes care of appending the note
// to the body after applying datas on the template
display( note )
}
else {
// loadTemplate just loads the template in an ajax request
// and executes the callback with the template as argument
loadTemplate( function( data ) {
// I fill in the template variable
template = data
// And I display the note since the template is available
display( note )
} )
}
}
So in this case it'll load three times the template, even though there is a check to prevent this. I guess this is because the three templates go straight into the else, but how can I prevent this?
I don't want to use sync ajax loading since this'd freeze the browser.
Edit: in the end, I've used @Managu's solution, slightly modified.
Instead of using his loop, I've used the following, much more elegant:
while ( backlog.length ) {
display( backlog.shift() )
}
Upvotes: 0
Views: 471
Reputation: 140228
var loadTemplate = function() {
var promise = $.get( "/" );
loadTemplate = function() {
return promise;
};
return promise;
};
Note that jQuery is not needed here, I just write as pseudocode. You can use any library that provides deferreds.
loadTemplate().then( function( data ) {
// I fill in the template variable
template = data
// And I display the note since the template is available
display( note )
} )
Upvotes: 2
Reputation: 6378
"I don't want to use sync ajax loading since this'd freeze the browser."
sync ajax will only freeze the browser if you try to use jQuery to do it. Frame.js was designed to solve problems exactly like you are having. There are multiple ways to resolve this issue with Frame, but they all involve some amount of sync and async mix. Likely the easiest way would be:
Frame(function(next){
loadTemplate(next);
});
Frame(function(next, data){ // data here is the value passed to loadTemplate's callback
notes.forEach( function( note ) {
template = data;
display( note );
});
next();
});
If you are not sure what the template is and it might be the same or different for each note, then you might do this instead:
notes.forEach( function( note ) {
Frame(function(next){
loadTemplate(note.templateName, next); // next here is a callback to advance to the next Frame
});
Frame(function(next, template){
display( note, template );
next();
});
});
That would load the templates synch, but do the display updating async.
Upvotes: 0
Reputation: 9039
Perhaps keep a backlog of work that needs to be done once the template is loaded? Basically a queue to smooth over the impedance mismatch caused by asynchrony.
var template;
var backlog;
function render(note)
{
if (template) {
// Template already loaded, just display.
display(note);
} else if (backlog) {
// Template being loaded, push work onto backlog.
backlog.push(note);
} else {
// Template not being loaded yet. Create backlog queue and launch request
// to load template.
backlog=[note];
loadTemplate(function(loaded_template) {
// Template finally came over the wire. Save it, and then
// work off the backlog.
template = loaded_template;
for (var note=backlog.shift();
backlog.length!=0;
note=backlog.shift())
{
display(note);
}
} );
}
}
Upvotes: 1