5

私は自分のRESTAPIをラップするjavascriptAPIを設計しています。私は一般的に、多くの冗長で紛らわしいネストされたコールバックを避けたいと思っており、DeferredjQueryの良さを読んでいます。

人のオブジェクトと人のオブジェクト間を移動する方法を表す私のライブラリ「myLib」を想像してみましょう。'dad'、'boss'、'assistant'などのメソッドがたくさんあり、データを見つけて別の関連する'people'オブジェクトを返すためにajaxリクエストを実行する必要があります。しかし、私は彼らに、私が一緒にチェーンできるmyLibのメソッドも持つ遅延オブジェクトを返してもらい、次のような非常に簡潔なコードを記述してもらいたいと思います。


 myLib('me').dad().boss().assistant(function(obj){
   alert(obj.phone); // My dad's, bosses assistants phone number
 }, function(){
   alert('No such luck);
 });

これにより、「me」personオブジェクトが作成され、最初のajax呼び出しを実行して私の詳細を検索し、そのデータを使用して別の呼び出しを実行して私の親を検索し、次にもう一度上司を検索し、次に別の呼び出しを実行してアシスタントを取得します。最後に、それは私のコールバックに渡され、私はそれを処理します。jQueryの連鎖トラバースメソッドに似ていますが、非同期です。

関数を任意の時点(通常は最後のメソッド)に渡すと、チェーン内の最後のDeferredオブジェクトが解決されたときに内部的に呼び出されます。2番目の関数は失敗コールバックであり、チェーン内の遅延オブジェクトのいずれかが拒否された場合に呼び出されます。

jQueryの遅延オブジェクトを作成して拡張する必要があると思いますが、それが「最善の」方法かどうかはわかりません。

では、ミニマリストAPIの目標を達成するためのベストプラクティスの方法は何ですか?基本的に、すべてのメソッド名をドメイン問題の名前空間で100%にし、「when」、「done」、「success」などの多くで汚染されないようにします。

そして、どこかでエミュレートできる同様のクリーンなAPIの例はありますか?

4

3 に答える 3

2

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

実装には2つの問題がありPerson.Chainます。getterメソッドを呼び出すたびに、実際にはnewを返す必要がPerson.Chainあり、そのnewPerson.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);
};

にgetterメソッドがあるのと同じ数のこれらのラッパーメソッドを定義する必要があります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());
}

まず、新しい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');
});

最初の呼び出しが失敗した場合、両方の失敗コールバックが順番に呼び出されます。最後の1つだけが失敗した場合、その1つだけが呼び出されます。

大きな注意点ですが、このコードはどれもテストされていません。しかし、理論的には、これは実用的なアプローチです。

于 2011-10-22T15:11:19.153 に答える
1

必要なのはクエリビルダーで、基準を追加するメソッド(「お父さん」や「アシスタント」など)はすべてチェーン可能です。また、いつでもコールバックを渡すことができるようにする必要があります。これは、クエリ。

だから私はこのようにします:

function PersonQuery(personName) {
  this.criteria = [];
  criteria.push({name:personName});
}

PersonQuery.prototype.dad = function(doneCallback) {
    this.criteria.push({relationship:"dad"});
    _execute(doneCallback);
    return this;
}

PersonQuery.prototype.boss = function(doneCallback) {
    this.criteria.push({relationship:"boss"});
    _execute(doneCallback);
    return this;
}

PersonQuery.prototype.assistant = function(doneCallback) {
    this.criteria.push({relationship:"assistant"});
    _execute(doneCallback);
    return this;
}

PersonQuery.prototype._execute = function(doneCallback) {
    if (!doneCallback) {
       return;
    }
    $.ajax({"data": this.criteria}).success(function(responseData) {
       doneCallback(responseData);   
    });
}

次に、これを使用すると、例は次のようになります。

   new PersonQuery("me").dad().boss().assistant(function(obj) { 
    alert(obj.phone); });
于 2011-10-22T17:31:17.133 に答える
0

これは内部の約束で完全に機能しているので、データのないPersonオブジェクトをすぐに作成します。そこに含まれているのは、後でデータを約束することだけです。親()のようなメソッドは、チェーンが現在のpromiseから外れた新しいpromiseを作成します。pipe()を使用してこれを簡単にする方法はおそらくありますが、まだ完全には理解されていません。


myLib = {
  find: function(id){
    var person = new Person();
    person.promise = $.ajax(.....);
  }
};

function Person(){
}
Person.prototype.parent(){
  var person = this;
  var parent = new Person();
  var deferred = $.Deferred();
  this.promise.then(function(data){
    var promise = $.ajax({url: '/person/'+data.parent+'/json'});
    promise.done(function(obj){
      person.data = obj.data;
      deferred.resolve(node);
    });
  }, function(error){
    deferred.fail();
  });
  parent.promise = deferred.promise();
  return parent;
}

Person.prototype.get(callback){
  this.promise.then(function(data){
    this.data = data;
    callback(this);
  });
}


Usage/Test:

myLib.find('12345').get(callback);
myLib.find('12345').parent().get(callback);



于 2011-10-23T22:53:44.510 に答える