Я собираюсь оставить свою реализацию 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
у нас есть две проблемы: каждый раз, когда вы вызываете метод получения, он действительно должен возвращать новый 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
. Теперь для реализации 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());
}
Сначала мы явно создаем новый отложенный объект и присоединяем к нему обработчики 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');
});
Если первый вызов завершится неудачно, оба обратных вызова будут вызваны по порядку. Если произойдет сбой только последнего, будет вызван только этот.
Большая оговорка, ни один из этого кода не проверен. Но теоретически , это рабочий подход.