user2754035
user2754035

Reputation: 67

How to realize a chain in JS?

I know that we can use "return this" in object, e.g.:

var $ = {
   name : function(){
     alert("John");
     return this;
  },
  age  : function(){
     alert(21);
     return this
  }
}

$.name().age()

When one function is completed next one will be called. But in JQuery there is such construction:

 $.get(url).done(callback).fail(callback);

How it works? How: method "get" gets callback on time. Ajax request is async by default.

P.S. I am interested in realization without $.Defferred object.

Thanks.

Upvotes: 1

Views: 135

Answers (1)

jfriend00
jfriend00

Reputation: 707786

Here's how $.get(url).done(callback); works.

$.get() returns an object. It returns that object immediately as soon as the async ajax call has been started (it does not wait until the ajax calls finishes). That object has a number of methods which includes the jQuery promise methods. One of those methods is .done(). When you call .done(callback) on that returned object it stores the callback in a list of callbacks in that returned object.

When the $.get() function finishes sometime later, it then .resolves() it's own promise which ends up looking for all the .done() callbacks stored in the object and calls the callbacks one at a time.

So, in this sequence of events, .done() is actually called immediately right after $.ajax() finishes initiating the ajax call, but all .done() does when it executes is store the callback for use later by $.ajax() when the async operation finishes.

Whether you know it or not, when you use the $.get(url).done(callback) construction, you are using a jQuery promise object and the promise API. It works pretty simply here because $.get() creates the promise object for you (and returns it and will then .resolve() or .fail() it later) and all you have to do is specify .done(), .fail() or .then() callbacks and most of the work is done for you. You don't have to manually create a deferred object or promise object - you can just use the one returned by the ajax call.


Here's a little async queue that supports chaining for visual effects with it's own .done() handler: http://jsfiddle.net/jfriend00/tS8G3/

function domWrapper(elem) {
    if (!(this instanceof domWrapper)) {
        return new domWrapper(elem);
    }
    if (typeof elem === "string") {
        elem = document.getElementById(elem);
    }
    this.elem = elem;
    this.q = [];
    this.timer = null;
    this.doneCallbacks = [];
}

domWrapper.prototype = {
    hide: function() {
        this.q.push({op: "hide"});
        this._exec();
        return this;
    },
    show: function() {
        this.q.push({op: "show"});
        this._exec();
        return this;
    },
    delay: function(t) {
        this.q.push({op: "delay", time: t});
        this._exec();
        return this;
    },
    done: function(fn) {
        this.doneCallbacks.push(fn);
        return this;
    },
    _exec: function() {
        var next;
        while (!this.timer && this.q.length) {
            next = this.q.shift();
            switch(next.op) {
                case "show":
                    this.elem.style.display = "block";
                    break;
                case "hide":
                    this.elem.style.display = "none";
                    break;
                case "delay":
                    var self = this;
                    this.timer = setTimeout(function() {
                        self.timer = null;
                        self._exec();
                    }, next.time);
                    break;
            }
        }
        // if all done with all pending activities
        if (!this.q.length && !this.timer) {
            // call the done callbacks
            for (var i = 0; i < this.doneCallbacks.length; i++) {
                this.doneCallbacks[i].call(this.elem);
            }
        }
    }
}

function makeBlue() {
    this.style.backgroundColor = "blue";
}

function makeBigger() {
    this.style.width = "200px";

}

domWrapper("test").done(makeBlue).done(makeBigger)
    .delay(2000).hide().delay(2000).show().delay(1000);

Upvotes: 4

Related Questions