Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programming pattern to flatten deeply nested ajax callbacks?

I've inherited JavaScript code where the success callback of an Ajax handler initiates another Ajax call where the success callback may or may not initiate another Ajax call. This leads to deeply nested anonymous functions. Maybe there is a clever programming pattern that avoids the deep-nesting and is more DRY. Also, there is the problem of inner variables myVar1 and myVar2 that are used throughout the functions.

jQuery.extend(Application.Model.prototype, {
    process: function() {
        var myVar1;
        // processing using myVar1;
        jQuery.ajax({
            url:myurl1,
            dataType:'json',
            success:function(data) {
                var myVar2;
                // process data using myVar1, set state of myVar2,
                // then send it back
                jQuery.ajax({
                    url:myurl2,
                    dataType:'json',
                    success:function(data) {
                        // do stuff with myVar1 and myVar2
                        if(!data.ok) {
                            jQuery.ajax({
                                url:myurl2,
                                dataType:'json',
                                success:mycallback
                            });
                        }
                        else {
                            mycallback(data);
                            }

                    }
                });
            }
        });
    }
});
like image 338
chiborg Avatar asked Apr 02 '10 00:04

chiborg


3 Answers

There's no need for all the callbacks to be anonymous and defined inline, you can declare them elsewhere and just use the function name when specifying the callback.

like image 59
cpalmer Avatar answered Nov 04 '22 00:11

cpalmer


Thanks to the chaining hint and this comment, I have come to the following solution. I have tested it and it works. There are probably some scope issues and you could refactor a general ChainAjax class out of it. But for the time being, this is ok.

jQuery.extend(MyApplication.Model.prototype, {
    process: function() {

        // private class for executing the Ajax calls
        var myAjaxCalls = function(options) {
          this.options = options;
          this.myVar1 = null;
          this.myVar2 =null;                
        }
        jQuery.extend(myAjaxCalls.prototype, {
          process1:function(data) {
            // processsing using this.myVar1
            this.myVar1 = 5;
            return true;
          },
          process2:function(data) {
            this.myVar2 = 6;    
            if(data.ok) {
                mycallback(data);
            }
            else {
                return true;
            }
          },
          process3:function(data) {
            // Process this.myVar1 and this.myVar 
            mycallback(data);
            return false;
          },
          chainAjax:function() {
            if(this.options.length > 0) {
              var opt = this.options.shift();
              var that = this;
              jQuery.ajax({
                url:opt.url,
                success:function(data) {
                  if(that[opt.callback](data)) {
                          that.chainAjax();
                  }
                }
              });
            }
          }
        });
        // End private class

        var calls = new myAjaxCalls([
            {url:'http://localhost/', callback:'process1'},
            {url:'http://localhost/', callback:'process2'},
            {url:'http://localhost/', callback:'process3'}
        ]);
        calls.chainAjax();
    }
});

Update: I found this nice presentation that also deals with useful programming patterns and best practices.

Update 2012: In the meantime there are several libraries for simulating a synchronous flow with asynchronous functions: q, stratified.js and streamline.js

like image 39
chiborg Avatar answered Nov 04 '22 02:11

chiborg


I would suggest creating a little tool called "chain ajax". You give it what you want to happen in what order, and then fire. It will chain ajax on success until all the logic runs out. It will help you stop repeating yourself and just represent the logical model of what you want done vs grunt-coding.

like image 34
Dmitriy Likhten Avatar answered Nov 04 '22 02:11

Dmitriy Likhten