1

I have a function with a return however in the function there is an async request which holds the value that is suppose to be returned by the function. I understand with the nature of async request the function will complete and not return a value while waiting on the async function to complete.

I attempted to use dojo deferred functions to have my function PostInformation() to return a value within the ajax request callback. I am having some issues and i am not sure where my issue is. Under is my code:

Dojo Deferred Function

function PostInformation(){

      var hasErrors = false;
        var containers = [dijit.byId("container1"), dijit.byId("container2")];
        var Employee = {
            //data
        };

    var def = new dojo.Deferred();

    def = dojo.xhrPost({
        url: 'hello',
        content: Employee,
        load: function (data) {

            formErrors = {
                "errors": true,
                "fName": "123",
                "surname": "456",
                "oNames": "789",
                "bSurname": "784585"
            };

            //formErrors = (JSON.parse(data)).formErrors;

            $.each(formErrors, function (key, value) {

                if (key == 'errors') {
                    hasErrors = value;
                    //console.log('hasErrors set to '+value);
                }
            });

            if (hasErrors == true) {
                for (var i = 0; i < containers.length; i++) {

                    var processingContainer = containers[i];

                    dojo.forEach(processingContainer.getChildren(), function (wid) {
                        var widgetName = wid.attr('id');

                        $.each(formErrors, function (key, value) {

                            if (key == widgetName && value.length > 0) {

                                var myWidget = dijit.byId(widgetName);
                                //var wdgName = dijit.byId(widgetName).attr("id");
                                var myWidgetValue = value;


                                myWidget.validator = function () {
                                    //console.log('Attribute Name is :' + wdgName + ' Error Value is : ' + myWidgetValue);
                                    //console.log(wdgName + " : "+myWidgetValue);
                                    this.set("invalidMessage", myWidgetValue);
                                };
                                myWidget._hasBeenBlurred = true;
                                myWidget.validate();
                            }
                        });
                    });
                }
            } 
            console.log(hasErrors);
            def.resolve(hasErrors);

        },
        error: function(err){
            console.log(err);
            def.reject(err);
        }

    });

    def.then(function(data){

         console.log('In the then function');       
        //alert('In the def.then and the results is : ' + data);
         if(data == true){           
             return false;
         }else{return true;}

    },function(err){
        return false;
        alert('In the def.error and there has been an error ' + err);
    }); 

    //return the value of hasErrors here
};
3
  • What are your issues exactly? Commented Apr 23, 2014 at 20:03
  • I am trying to return hasErrors value which was set in ajax call back function Commented Apr 23, 2014 at 20:11
  • The value of data in def.then function is not accessible outside of that callback Commented Apr 23, 2014 at 20:14

3 Answers 3

2

Devdar, you are making heavy wether out of something quite simple. In particular, you don't need to loop through an object to access one of its properties, and the variable hasErrors is not really necessary.

Your code should simplify to something like this :

function PostInformation() {
    var $containers = $("#container1, #container2");
    var Employee = {
        //data
    };
    return dojo.xhrPost({
        url: 'hello',
        content: Employee
    }).then(function(data) {
        data = JSON.parse(data);
        var formErrors = data.formErrors;
        if(formErrors.errors) {
            $containers.each(function(i, c) {
                $(c).children().each(function(wid) {
                    var val = formErrors[wid.id],
                        myWidget;
                    if(val) {
                        myWidget = dijit.byId(wid.id);
                        myWidget.validator = function() {
                            this.set("invalidMessage", val);
                        };
                        myWidget._hasBeenBlurred = true;
                        myWidget.validate();
                    }
                });
            });
            //Send an enhanced error object down the "error" route 
            throw $.extend(formErrors, {
                'message': 'PostInformation(): validation failure'
            });
        }
        //Send the data object down the "success" route 
        return data;
    });
};

PostInformation().then(function(data) {
    console.log('PostInformation(): everything went OK');
    //access/process `data` here if necessary
    //and/or just display a nice "success" message to the user
}, function(err) {
    console.error(err.message);
});

Barring mistakes on my part, this code should do everything you want and more. As with your own code, it processes the server's JSON response and returns a Promise, but that's where the similarity stops.

In your code, you seek to return a Promise which is eventually resolved with a boolean to indicate whether or not errors were detected. Whilst this will (if correctly written) meet your immediate needs, it is not the best Promise logic.

In my code, the Promise is resolved only if validation succeeds and rejected if validation fails for whatever reason. Not only is this logically correct behaviour for a Promise (success goes down the success route, and errors go down the error route) but as a bonus should (see note below) also allow you to pass more information to whetever function(s) eventually handle errors. I choose to pass the whole formErrors object enhanced with an error message, thus providing a great deal of freedom in the error handler to display/log/etc as much or as little as is appropriate, and with virtually no assumption inside PostInformation() as to what will happen subsequently. You currently believe that you will only read and act on the boolean formErrors.errors but it could be beneficial to pass as much error data as possible thus allowing yourself the freedom to change your mind at a later date without needing to change anything in PostInformation().

In this regard you can think of PostInformation() as an agent of the server-side service; and like that service, it can be written with incomplete knowledge (or maybe no knowledge at all) of how the (promise of) data/errors it delivers will be used by "consumer code".


NOTE: I have to admit that I'm not 100% familiar with Dojo's Promises, so I'm not sure that a JS plain object can be thrown in the way I indicate. I have found evidence but not proof that it can. For that reason, I am cautious above in saying "your code should simplify to something like this" Anyway, that issue aside, the principle of sending success down the success route and errors down the error route should still apply.

Sign up to request clarification or add additional context in comments.

4 Comments

I understand your code however I don't think it may do what I need it to and please feel free to correct me. The mail goal of the function PostInformation is to return a true or false value based on the error attribute in the data object of the callback function in .then. However because the ajax request is async. The function will execute to the end and not return a value whistle the ajax request is still executing. This is where the issue is. By the time the ajax request completes the callback function it is too late since the function would not have returned a value.
So I needed to use a sync request to wait until the value is received. Then the function PostInformation will have a true or false value to return. Data is only accessible within the callback method. So the issue was the request type it needed to be sync. So a value could be returned at the root of the function.
Please feel free to correct me or suggest a better approach than a sync request. I will test your solution and let you know how it works for my needs
Devdar, attempting to answer your questions, I wrote more than will fit in a SO Comment, so have added explanatory text below the code block in the answer. Hope it helps.
1

I'd suggest this where you create your own Deferred() object, return it from your PostInformation() function and then register .then() handlers on it so you can pick up the resolve or reject on your own Deferred object that happens inside the PostInformation() function.

The way you had it you were creating your own Deferred() object, but then immediately overwriting it with the xhrPost return result which meant def is now something else and you weren't returning your Deferred from PostInformation() so it can be used outside that function to track the progress.

function PostInformation() {
    var hasErrors = false;
    var containers = [dijit.byId("container1"), dijit.byId("container2")];
    var Employee = {
        //data
    };
    var def = new dojo.Deferred();
    dojo.xhrPost({
        url: 'hello',
        content: Employee,
        load: function (data) {
            formErrors = {
                "errors": true,
                "fName": "123",
                "surname": "456",
                "oNames": "789",
                "bSurname": "784585"
            };
            //formErrors = (JSON.parse(data)).formErrors;
            $.each(formErrors, function (key, value) {
                if (key == 'errors') {
                    hasErrors = value;
                    //console.log('hasErrors set to '+value);
                }
            });
            if (hasErrors == true) {
                for (var i = 0; i < containers.length; i++) {
                    var processingContainer = containers[i];
                    dojo.forEach(processingContainer.getChildren(), function (wid) {
                        var widgetName = wid.attr('id');
                        $.each(formErrors, function (key, value) {
                            if (key == widgetName && value.length > 0) {
                                var myWidget = dijit.byId(widgetName);
                                //var wdgName = dijit.byId(widgetName).attr("id");
                                var myWidgetValue = value;
                                myWidget.validator = function () {
                                    //console.log('Attribute Name is :' + wdgName + ' Error Value is : ' + myWidgetValue);
                                    //console.log(wdgName + " : "+myWidgetValue);
                                    this.set("invalidMessage", myWidgetValue);
                                };
                                myWidget._hasBeenBlurred = true;
                                myWidget.validate();
                            }
                        });
                    });
                }
            }
            console.log(hasErrors);
            def.resolve(hasErrors);
        },
        error: function (err) {
            console.log(err);
            def.reject(err);
        }
    });
    return def.promise;
};

PostInformation().then(function (data) {
     console.log('In the then function');
     // process data value here which will contain the value you resolved with
  }, function(err)
      // process an error in the ajax result here
});

5 Comments

i need to return the true of false value
@devdar - I was confused by your indentation. I've fixed it. You CAN'T return true or false from PostInformation() as the result of the ajax call. It contains an asynchronous function that will be complete only sometime LATER, long AFTER PostInformation() has finished executing. That's where deferreds can help you. If you return a deferred, then that deferred can be monitored for when the data is actually available.
can I access the deferrer using def.isFulfilled() to see if it had been completed ? Once that is done how can I access hasErrors variable hasErrors?
@devdar - In the code I proposed for you, then first callback f1 as in .then(f1, f2) is called when the deferred is resolved and it is called with the arguments passed to the resolve() method which in your case is the value of hasErrors. The second callback f2 to .then(f1, f2) is what is called if the promise is rejected (which your code triggers if the ajax call fails). So, you don't have to call def.isFulfilled() because if the first callback was called, then the promise was resolved successfully and you can then look at the arguments to that first function.
@devdar - See the dojo doc here for more info: dojotoolkit.org/reference-guide/1.9/dojo/Deferred.html
0

I think this is more of an issue with design of the function then.

Since the xHR call is asynchronous, the postInformation shouldn't really return anything unless it's the Deferred object itself. An alternative option is to have postInformation do some sort of event publishing (dojo/topic), that other functions will subscribe to and know how to handle said events.

Comments

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.