Florian Margaine
Florian Margaine

Reputation: 60767

Async template loading - How to load it only once?

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

Answers (3)

Esailija
Esailija

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

BishopZ
BishopZ

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

Managu
Managu

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

Related Questions