47

@Domenic には、jQuery 遅延オブジェクトの失敗に関する非常に詳細な記事があります: You're missing the Point of Promises。その中で Domenic は、Q、when.js、RSVP.js、および ES6 の promise を含む他のものと比較して、jQuery の promise のいくつかの失敗を強調しています。

私はドメニックの記事から離れて、jQuery の約束には概念的に本質的な失敗があると感じています。私は概念に例を挙げようとしています。

jQuery の実装には 2 つの懸念事項があると思います。

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

言い換えると

promise.then(a).then(b)

が満たされるとjQueryが呼び出さaれます。bpromise

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

 

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


編集 この質問はjQuery < 3.0に関連しています。jQuery 3.0 alphaの時点で、jQuery は Promises/A+ に準拠しています。

4

1 に答える 1

54

更新: jQuery 3.0 では、以下に概説する問題が修正されました。Promises/A+ に真に準拠しています。

はい、jQuery の promise には深刻な固有の問題があります。

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

したがってreturnsPromise().then(a).then(b)、プロミスを返す関数のjQueryでも、期待どおりに機能ab、戻り値をアンラップしてから先に進みます。このフィドルに示されているように:

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 つの大きな問題は、エラー処理と予期しない実行順序です。

エラー処理

catch とは異なり、解決したとしても、拒否された jQuery promise を「処理済み」としてマークする方法はありません。これにより、jQuery での拒否は本質的に壊れており、使用が非常に困難になり、 synchronous とは異なり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+ ライブラリで得られるフローであり、有用性に期待するものです。これは非常に大きく、スローの安全性はプロミスの大きなセールス ポイントです。この場合、Bluebirdは正しく動作しています。

実行順序

jQuery は、基になる promise が既に解決されている場合、渡された関数を延期するのではなく、すぐに実行します。そのため、ハンドラーをアタッチしている promise が既に解決されているかどうかに応じて、コードの動作が異なります。これは事実上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 が常にイベント ループの次の実行まで延期されることを要求する理由です。

サイドノート

.doneBluebird (および実験的に When) のような新しくて強力なプロミス ライブラリは、Q のようにチェーンの最後で必要としないことに言及する価値があります。

于 2014-05-19T18:31:33.850 に答える