0

I have read allot about function expressions vs declaration, callbacks, hoisting, and I get the general idea of most of this but I guess I can't quite grasp the concept because of the below code, let me post the code and then ask the real questions.

    var url = "beverages.txt";


   var GridModel = function () {
        this.items = ko.observableArray();
        var me = this;
        $.ajax({
            datatype: 'json',
            url: "beverages.txt"
        }).done(function (data) {
            debugger;
            var jsonData = $.parseJSON(data);
            me.items(jsonData);
        });
    };


    var model = new GridModel();

    // prepare the data
    var source =
    {
        datatype: "observablearray",
        datafields: [
            { name: 'name' },
            { name: 'type' },
            { name: 'calories', type: 'int' },
            { name: 'totalfat' },
            { name: 'protein' },
        ],
        id: 'id',
        localdata: model.items
    };

    var dataAdapter = new $.jqx.dataAdapter(source);

    $("#grid").jqxGrid(
    {
        width: 670,
        source: dataAdapter,
        theme: 'classic',
        columns: [
          { text: 'Name', datafield: 'name', width: 250 },
          { text: 'Beverage Type', datafield: 'type', width: 250 },
          { text: 'Calories', datafield: 'calories', width: 180 },
          { text: 'Total Fat', datafield: 'totalfat', width: 120 },
          { text: 'Protein', datafield: 'protein', minwidth: 120 }
        ]
    });

    ko.applyBindings(model);
});

Ok so this code works fine, it calls the ajax request by var model = new GridModel();. The problem is that if I add a debugger; statement after var model = new GridModel(); it fails. Also the debugger statements inside of the ajax request don't fire, however if I remove the debugger statement after the var model = new GridModel(); then ajax fires and I can debug the request. Why does it fail with the additional debugger, is it because var GridModel is an Expression.

Basically want I would like to do is is create a declaration function that I can call and when ajax request is done I return the observableArray me. If I change the function like this

  function GridModel (param1,param2) {
            this.items = ko.observableArray();
            var me = this;
            $.ajax({
                datatype: 'json',
                url: "beverages.txt"
            }).done(function (data) {
                debugger;
                var jsonData = $.parseJSON(data);
                me.items(jsonData);
            });
            return me
        };

Then I would like to just be able to call that function like this var myitems = GridModel(param1,param2) with the expectation that myitems will now hold the results of the ajax request. I just don't fully understand how the code execution flow works, If someone could explain why the bottom function doesn't work and how to get it to work I would appreciate it.

Thanks, Dan

0

2 Answers 2

3

If you have an asynchronous operation (like an Ajax request), and everything else depends on its result, resume your program's flow from the callback. You can't use return statements, it only works for synchronous code.

You may want to modify your GridModel constructor to take a callback as a parameter:

var GridModel = function (callback) {
    this.items = ko.observableArray();
    $.ajax({
        datatype: 'json',
        url: "beverages.txt"
    }).done(callback);
};

Then resume your program flow from inside the callback:

function resumeMyProgramFlow(data) {
    // Now you can use the data, resume flow from here
    debugger;
    var jsonData = $.parseJSON(data);
    model.items(jsonData);
    // etc.
}

And instantiate GridModel like this:

var model = new GridModel(resumeMyProgramFlow);
Sign up to request clarification or add additional context in comments.

10 Comments

I think I understand what you are saying and I just tried it in code but I am running into the same problem. var model = new GridModel(resumeMyProgramFlow); model is an empty object. Also if I add a debugger statment after var model = new GridModel(resumeMyProgramFlow); then ajax never executes. Sorry that I don't understand, thanks for your help!
Model will be an empty GridModel instance, with an empty observableArray inside. I'm not sure if the Ajax request is held by the debugger, but that may be the case. My original code had a mistake I just fixed. Other than that, I think all the rest of your code (after var model...) should go inside the callback.
model is inside the callback so it is not defined yet. Trying to figure it out now and put the code in the callback as you suggested
It's defined at the top of the scope (because of hoisting). And the callback will only run after var model line; by then, it will already be assigned a value, and will be available to the callback via closure (I know,it's confusing)
Ok first problem was that my resumeProgramFlow I had in another js file and not in the main js file. I moved it and now model is defined. Just trying to wrap my head around this. So var model is initial defined as an empty GridModel because of hoisting, then when the callback function is fired, I can fill model with data. Think I finally understand. I should be able to pass in addition parameters to new GridModel(resumeMyProgramFlow); like GridModel(param1,param2,resumeMyProgramFlow); correct? Thanks again for your help
|
2

When $.ajax(...).done(...) returns, the program flow continues.

But the ajax call isn't finished yet, because in the background it sends data to the server and waits for a response. This is the asynchronous part. When the response finally arrives from the server, the part in .done(...) is executed

function (data) {
    debugger;
    var jsonData = $.parseJSON(data);
    me.items(jsonData);
}

This is where and when the returned data is processed.

What you describe with var myitems = GridModel(param1,param2) is a synchronous method. It calls to the server stops further processing and waits for the response.

While this is possible, it also blocks the whole program flow. That's why an ajax call is asynchronous and the response dealt with in a callback function.

What you want to do synchronously

var myitems = GridModel(param1,param2);
// do something with myitems

is done asynchronously this way

$.ajax(...).done(function(data) {
    // 1. do something with myitems
});

// 2. this will run usually *before* number 1. above

3 Comments

Ok, makes sense. What I am trying to accomplish is to make the var GridModel = function more generic so that I just have one function defined that I can pass in parameters to such as url that returns the ko observable array that I can then pass into the source, source is currently hard coded but I will also have a function that returns the source that I would like to pass in the model.items. Hope this makes sense
@dan I tried to explain it with some code. Please see updated answer.
Thanks, yes I do understand that I was trying to not have to put all my code in the done function, thanks for the explanation

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.