29

単一の引数を取る関数があります。Promiseこの引数がjQueryなのかDeferredオブジェクトなのかを見分ける必要があります。そうでない場合、値は任意のタイプであり、任意のプロパティを持っている可能性があるため、promiseメソッドが存在するだけでは安全ではありません。

関数をどのように動作させたいかの例を次に示します。

function displayMessage(message) {
  if (message is a Promise or Deferred) {
    message.then(displayMessage);
  } else {
    alert(message);
  }
}

promiseの再帰的な処理に注意してください。promiseが表示されない別のpromise値で解決された場合、それが解決されるのを待ちます。さらに別の約束が返される場合は、繰り返します。


これが当てはまらない場合は、次を使用できるため、これは重要ですjQuery.when

function displayMessage(message) {
  jQuery.when(message).then(function(messageString) {
    alert(messageString);
  });
}

これは、値と値の約束を正しく処理します...

displayMessage("hello");                            // alerts "hello"
displayMessage(jQuery.Deferred().resolve("hello")); // alerts "hello"

...しかし、価値の約束の約束に達すると、それは崩壊します:

displayMessage(jQuery.Deferred().resolve(
  jQuery.Deferred().resolve("hello")
));                                                 // alerts "[object Object]"

jQuery.when値が約束であるかどうかを知ることができるので、明らかにそれは可能です。どうすれば確認できますか?

4

2 に答える 2

32

jQuery.when値が約束であるかどうかを知ることができるので、明らかにそれは可能です。

これは間違っています。jQuery自体は、オブジェクトが完全な精度でPromiseであるかどうかを確認できません。jQuery.when jQueryソースビューアでのソースを見ると、それが行うのはこれだけであることがわかります。

jQuery.isFunction(firstParam.promise)

返すオブジェクトに独自の.promise()メソッドがある場合、jQuery.when誤動作します。

var trickyValue = {
  promise: function() { return 3; },
  value: 2
};

jQuery.when(trickyValue).then(function(obj) {
  alert(obj.value);
});

TypeError: Object 3 has no method 'then'jQueryはオブジェクトがpromiseであると想定し、その.promise()メソッドの値を信頼するため、これはスローします。

これを適切に解決することはおそらく不可能です。jQuery.Deferredpromiseオブジェクトは、 (view source )内のオブジェクトリテラルとして作成されます。プロトタイプも、それを区別するために使用できる他の真にユニークなプロパティもありません。

ただし、jQueryの1つのバージョンのみが使用されている限り、信頼できるはずのハッキーなソリューションを考えることができます。

function isPromise(value) {
  if (typeof value === 'object' && typeof value.then !== "function") {
    return false;
  }
  var promiseThenSrc = String($.Deferred().then);
  var valueThenSrc = String(value.then);
  return promiseThenSrc === valueThenSrc;
}

isPromise("test");                 // false
isPromise($.Deferred());           // true
isPromise($.Deferred().promise()); // true

関数を文字列に変換するとそのソースコードが得られるので、ここで.thenは、新しいオブジェクトのメソッドのソースを、関心のある値のソースと比較します。値には、まったく同じメソッドDeferredはありません。.thenソースコードをajQuery.DeferredまたはPromise1として。

1.敵対的な環境で実行している場合を除いて、その場合はおそらくあきらめる必要があります。


jQueryのpromiseには特に関心がないが、ECMAScript 6の組み込みのものを含め、あらゆるタイプのPromiseを検出したい場合は、valueがオブジェクトであり、thenメソッドがあるかどうかをテストできます。

if (typeof value === 'object' && typeof value.then === 'function') {
  // handle a promise
} else {
  // handle a concrete value
}

これは、ES6で定義されているいくつかのPromise処理関数によるアプローチです。これは、以下に部分的に引用されている関数の仕様でresolve(...)説明されています。

引数解決を使用してpromise解決関数Fが呼び出されると、次の手順が実行されます。

[...]

  1. Type(resolution)がObjectでない場合、
    1. FulfillPromise(promiseresolution)を返します。
  2. 次に、Get(resolution"then")とします。
  3. それが突然の完了である場合、
    1. RejectPromise(promisethen。[[value]])を返します。
  4. thenActionをthen。[[value]]とします。
  5. IsCallable(thenAction)がfalseの場合、
    1. FulfillPromise(promiseresolution)を返します。
  6. EnqueueJobを実行します("PromiseJobs"、PromiseResolveThenableJob、«‍promise、resolution、thenAction»
于 2012-10-25T19:40:59.153 に答える
14

手っ取り早い解決策は、オブジェクトに次のthen機能があるかどうかをテストすることです。

if (typeof message.then === 'function') {
    //assume it's a Deferred or fits the Deferred interface
} else {
    //do other stuff
}
于 2012-10-25T19:20:22.897 に答える