web-dev-qa-db-ja.com

jQuery $ .Deferred固有の問題(jQuery 1.x / 2.x)

@Domenicには、jQuery遅延オブジェクトの障害に関する非常に詳細な記事があります。 あなたはPoint of Promisesがありません 。その中で、Domenicは [〜#〜] q [〜#〜] 、when.js、RSVP.js、ES6の約束など、他と比較してjQueryの約束のいくつかの失敗を強調しています。

私は、jQueryの約束には固有の失敗が概念的にはあると感じているDomenicの記事から離れます。私は概念に例を入れようとしています。

JQueryの実装には2つの懸念事項があります。

1. .thenメソッドはチェーン可能ではありません

言い換えると

promise.then(a).then(b)

jQueryは、aが満たされると、bを呼び出し、次にpromiseを呼び出します。

.thenは他のpromiseライブラリで新しいpromiseを返すため、同等のものは次のようになります。

promise.then(a)
promise.then(b)

2.例外処理はjQueryでバブルされます。

もう1つの問題は、例外処理、つまり次のように思われます。

try {
  promise.then(a)
} catch (e) {
}

Qに相当するものは次のとおりです。

try {
  promise.then(a).done()
} catch (e) {
   // .done() re-throws any exceptions from a
}

JQueryでは、aがcatchブロックに失敗すると例外がスローされ、バブルします。他の約束では、aの例外は.doneまたは.catchまたは他の非同期キャッチに引き継がれます。 promise API呼び出しのいずれも例外をキャッチしない場合、例外は消えます(したがって、.doneを使用して未処理の例外を解放するというQのベストプラクティスです)。

上記の問題は、約束のjQuery実装に関する懸念をカバーしていますか、それとも問題を誤解したか、見落としていましたか?


Editこの質問はjQuery <3.0に関連しています。 jQuery 3.0 alpha jQueryはPromises/A +に準拠しています。

45
Brian M. Hunt

更新:jQuery 3.0は、以下に概説する問題を修正しました。 Promises/A +に完全に準拠しています。

はい、jQueryの約束には深刻で固有の問題があります。

とは言っても、この記事は書かれているので、jQueryはより多くのPromises/Aplusの苦情に対応するために多大な努力を払っており、現在では.thenメソッドが連鎖しています。

したがって、jQueryでも、aおよびb関数を返すpromiseのreturnsPromise().then(a).then(b)は期待どおりに機能し、戻り値をラップ解除してから先に進みます。このように fiddle

function timeout(){
    var d = $.Deferred();
    setTimeout(function(){ d.resolve(); },1000);
    return d.promise();
}

timeout().then(function(){
   document.body.innerHTML = "First";
   return timeout();
}).then(function(){
   document.body.innerHTML += "<br />Second";
   return timeout();
}).then(function(){
   document.body.innerHTML += "<br />Third";
   return timeout();
});

ただし、jQueryの2つのhuge問題は、エラー処理と予期しない実行順序です。

エラー処理

Catchとは異なり、解決したとしても、「処理済み」として拒否されたjQuery Promiseをマークする方法はありません。これにより、jQueryの拒否が本質的に壊れ、使用が非常に難しくなります。同期try/catch

ここで何のログを推測できますか? ( フィドル

timeout().then(function(){
   throw new Error("Boo");
}).then(function(){
   console.log("Hello World");
},function(){
    console.log("In Error Handler");   
}).then(function(){
   console.log("This should have run");
}).fail(function(){
   console.log("But this does instead"); 
});

"uncaught Error: boo"あなたは正しかった。 jQuery Promiseはスローセーフではありません。 Promises/Aplus Promiseとは異なり、スローされたエラーを処理できません。安全性の拒否はどうですか? ( フィドル

timeout().then(function(){
   var d = $.Deferred(); d.reject();
   return d;
}).then(function(){
   console.log("Hello World");
},function(){
    console.log("In Error Handler");   
}).then(function(){
   console.log("This should have run");
}).fail(function(){
   console.log("But this does instead"); 
});

次のログ"In Error Handler" "But this does instead"-jQuery Promiseの拒否を処理する方法はまったくありません。これは、予想されるフローとは異なります。

try{
   throw new Error("Hello World");
} catch(e){
   console.log("In Error handler");
}
console.log("This should have run");

これは、BluebirdやQなどのPromises/A +ライブラリで得られるフローであり、有用性に期待するものです。これはhugeであり、スローの安全性は約束の大きなセールスポイントです。 この場合正しく動作するBluebird です。

実行順序

jQueryは、基礎となるプロミスが既に解決されている場合、渡された関数immediatelyを延期するのではなく実行します。これは事実上 Zalgoのリリース であり、最も痛いバグのいくつかを引き起こす可能性があります。これにより、デバッグが最も難しいバグがいくつか発生します。

次のコードを見ると:( fiddle

function timeout(){
    var d = $.Deferred();
    setTimeout(function(){ d.resolve(); },1000);
    return d.promise();
}
console.log("This");
var p = timeout();
p.then(function(){
   console.log("expected from an async api.");
});
console.log("is");

setTimeout(function(){
    console.log("He");
    p.then(function(){
        console.log("̟̺̜̙͉Z̤̲̙̙͎̥̝A͎̣͔̙͘L̥̻̗̳̻̳̳͢G͉̖̯͓̞̩̦O̹̹̺!̙͈͎̞̬ *");
    });
    console.log("Comes");
},2000);

setTimeoutは非常に危険な動作であるため、元のタイムアウトが終了するまで待機するため、jQueryは実行順序を切り替えます。なぜなら、スタックオーバーフローを引き起こさない決定論的なAPIが好きなのは誰か?これが、Promises/A +仕様で、promiseが常にイベントループの次の実行まで延期されることを要求している理由です。

サイドノート

Bluebird(および実験的にはWhen)のような新しく強力なpromiseライブラリは.done Qのようにチェーンの最後にあるのは、未処理の拒否自体を把握しているためです。また、jQueryの約束やQの約束よりもはるかに高速です。

52