chiborg
chiborg

Reputation: 28074

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);
                            }

                    }
                });
            }
        });
    }
});

Upvotes: 6

Views: 2489

Answers (4)

BishopZ
BishopZ

Reputation: 6378

You could do this with Frame.js like this:

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

Upvotes: 0

chiborg
chiborg

Reputation: 28074

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

Upvotes: 3

Dmitriy Likhten
Dmitriy Likhten

Reputation: 5206

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.

Upvotes: 1

cpalmer
cpalmer

Reputation: 1117

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.

Upvotes: 9

Related Questions