@Domenicには、jQuery遅延オブジェクトの障害に関する非常に詳細な記事があります。 あなたはPoint of Promisesがありません 。その中で、Domenicは [〜#〜] q [〜#〜] 、when.js、RSVP.js、ES6の約束など、他と比較してjQueryの約束のいくつかの失敗を強調しています。
私は、jQueryの約束には固有の失敗が概念的にはあると感じているDomenicの記事から離れます。私は概念に例を入れようとしています。
JQueryの実装には2つの懸念事項があります。
.then
メソッドはチェーン可能ではありません言い換えると
promise.then(a).then(b)
jQueryは、a
が満たされると、b
を呼び出し、次にpromise
を呼び出します。
.then
は他のpromiseライブラリで新しいpromiseを返すため、同等のものは次のようになります。
promise.then(a)
promise.then(b)
もう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 +に準拠しています。
更新:jQuery 3.0は、以下に概説する問題を修正しました。 Promises/A +に完全に準拠しています。
とは言っても、この記事は書かれているので、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();
});
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の約束よりもはるかに高速です。