我不打算继续修改我的Person实现,因为我认为它基本上已经完成了它的目标:
function Person(o) {
this.id = o.id;
this.name = o.name;
}
Person.prototype.dad = function(done, fail) {
var promise = $.getJSON('/people/' + this.id + '/dad').pipe(Person, null);
promise.then(done, fail);
return new Person.Chain(promise);
};
Person.prototype.boss = function(done, fail) {
var promise = $.getJSON('/people/' + this.id + '/boss').pipe(Person, null);
promise.then(done, fail);
return new Person.Chain(promise);
};
对于
Person.Chain
的实现,我们有两个问题:每次调用getter方法时,它都应该返回一个新的
Person.Chain
,而这个新的
Person.Chain
应该是“嵌套”的:它需要将AJAX调用的结果链接在一起。这样可以解决这两个问题。
这种方法需要编写一些粘合代码,因此首先让我们确保我们不必一遍又一遍地重复它。
Person.Chain = function(promise) {
this.promise = promise;
};
Person.Chain.prototype.assistant = function(done, fail) {
return this.pipe('assistant', done, fail);
};
Person.Chain.prototype.dad = function(done, fail) {
return this.pipe('dad', done, fail);
};
Person.Chain.prototype.boss = function(done, fail) {
return this.pipe('boss', done, fail);
};
我们只需要定义与
Person
的 getter 方法数量相同的包装方法。 现在,来实现
pipe
:
Person.Chain.prototype.pipe = function(f, done, fail) {
var defer = new $.Deferred();
defer.then(done, fail);
this.promise.pipe(function(person) {
person[f](function(person) {
defer.resolve(person);
}, function() {
defer.reject();
});
}, function() {
defer.reject();
});
return new Person.Chain(defer.promise());
}
首先,我们明确创建一个新的Deferred对象,并将
done
和
fail
处理程序(如果有)附加到它。然后,我们附加一个函数,该函数将在
f
被传递给
Person
并从前一个函数返回时进行调用(父亲、助手、老板等)。最后,当
那个函数解析时,我们明确解析我们创建的Deferred对象。现在我们可以像这样链接连续的调用:
jake = new Person({id: 3, name: 'Jake'});
jake.dad().boss().assistant(function(person) {
alert("Jake's dad's boss's assistant is " + person.name);
});
请注意,失败处理有些冗长,但我们需要这样做,以便如果您将一堆调用链接在一起,早期的失败仍然会将其'reject()'调用沿着给定的失败回调传递到最后。
这样做也是完全合法的:
jake.dad(function(person) {
alert('Dad is ' + person.name);
}, function() {
alert('Dad call failed');
}).boss(function(person) {
alert('Jake dad boss is ' + person.name);
}, function() {
alert('One of the calls failed');
});
如果第一次调用失败,两个失败回调函数将按顺序被调用。如果只有最后一个失败,那么只有最后一个回调函数会被调用。
需要注意的是,这段代码没有经过测试。但是,理论上它应该是可行的。