HOW TO: check if callbacks are running

Today I was struggling with a nasty bug. The functionality is pretty basic: we attach an onchange event handler on several textboxes in which we do a callback with $.ajax. During the callback, we make some calculations on the server-side and when the callback comes back successfully, we update the textboxes with the new values.

This works fine but we encountered a problem when the cursor stays in the textbox where the value was changed and the user clicks directly a save button, which is used to commit the changes to the database. In this scenario, the onchange event handler is triggered, which then makes a callback, but in the same time the button also posts back to the server. Some of the textboxes are still not updated by the callback’s success so the old values will be saved in the database.

I had several ideas in mind on how to fix this but we finally decided to do the following: on the button’s client click event handler we attach a function which checks if any ajax requests are currently running. If yes, then we do a short timeout and most importantly return false so that the button doesn’t post back. The first parameter in the setTimeout function is a function which will be called when the timeout is over. So we set that same function in the timeout function in order to check again if the ajax callback is finished. We do this until no active callbacks are running anymore and then finally trigger the button’s postback. Here’s the little piece of code which is doing all the magic:

function WaitAsyncBeforePostback(senderId, args, delay) {
    if ($.active > 0) {
        setTimeout(
        String.format(
            WaitAsyncBeforePostback(
                '{0}', '{1}', {2});", 
                senderId, 
                args, 
                delay), 
        delay);
        return false;
    }
    else {
        __doPostBack(senderId, args);
    }
}
 

It took me some time to find out how to check if any callbacks are running but I’m very glad to have this “active” field from jQuery which returns the number of ajax callbacks currently running.

If you have server-side click event handler attached to your button but even if you don’t, you must pay attention to how you’re calling the __doPostBack.

Let’s say if you do this for example:

<asp:Button runat="server" ID="SaveItemInfoButton" 
    Text="Save" OnClick="SaveButtonClick" 
    OnClientClick=
"return WaitAsyncBeforePostback(this.id, '', 100);" />
 

It will not work because the SaveButtonClick will not be called on the server-side.

When a button is rendered out on the client side, it has an ID and a name. The client-side ID represents the server-side ClientID and the name is the client-side equivalent of the server-side UniqueID. We need to provide the UniqueID to the _doPostBack so the following code will poll the postback until all callbacks are finished, do the postback AND call the SaveButtonClick:

<asp:Button runat="server" ID="SaveItemInfoButton" 
   Text="Save" OnClick="SaveButtonClick" 
   OnClientClick=
"return WaitAsyncBeforePostback(this.name, '', 100);" />

 

Finally, because jQuery is already used widely, I think the jQuery documentation needs some improvement. Every time I need something, I can’t find it but have to search blogs instead.

Advertisements

3 thoughts on “HOW TO: check if callbacks are running

  1. Monika: Whenever you find yourself passing a string to setTimeout, step away from the code for a while. You’re doing things the old way. Here’s how I would approach it:

    var $$ = {  // just for testing
      active: 1
    };
    
    var __doPostBack = function(a, b) {
      // just for testing
      console.log("post back");
    };
    
    function WaitAsyncBeforePostback(senderId, args, delay) {
        var callback = function() {
          if ($$.active > 0) {
            console.log("*"); // to see the timeouts
            setTimeout(callback, delay);
            return false;
          }
          __doPostBack(senderId, args);
          return true;
        };
    
        return callback();
    }; 
    
    WaitAsyncBeforePostback(12, 12, 3000);
    

    So, in essence, write an inner function to do the callback thing and because of the magic of closures it’ll keep a reference to the parameters or the outer function.

    Cheers, Julian

  2. Thanks a lot for your suggestion. I also agree that it’s a better approach.

    I’m actually following the webinars you’re doing on Javascript and you were discussing closures yesterday. Shame on me for not putting it to practice now! 🙂

    Anyhow I also recognize that sometimes I’m doing things old-fashioned but I’ll try hard(er) to get used to this new way.

Comments are closed.