ホームページ > 記事 > ウェブフロントエンド > Jqueryのajax同期が原因でUIスレッドがブロックされる問題
最近プロジェクトに取り組んでいるときに、同期された Ajax が原因で UI スレッドがブロックされるという問題が発生しました。その問題解決プロセスを共有したいと思います。
その理由は、コードの再利用性を向上させるために、さまざまなパラメーターを受け取り、データの取得のみを行う getData という関数をカプセル化したためです。そしてデータを返します。取り除かれた基本ロジックは次のとおりです:
function getData1(){ var result; $.ajax({ url : "p.php", async : false, success: function(data){ result = data; } }); return result; }
ここでの ajax は非同期にすることはできません。そうでない場合、関数が返されたときに結果に値が割り当てられていないため、エラーが発生します。そこで、async:falseを追加しました。問題はないようです。この関数を呼び出すと正常にデータを取得できます。
$(".btn1").click(function(){ var data = getData1(); alert(data); });
次に、ajax リクエストは時間がかかるので、リクエストを送信する前にページに読み込み効果を与える必要があります。つまり、「読み込み中」の gif 画像を表示する必要があると思います。それも見たでしょう。したがって、私の処理関数は次のようになります:
$(".btn1").click(function(){ $(".loadingicon").show(); var data = getData1(); $(".loadingicon").hide(); alert(data); });
リクエスト前に読み込み中の画像を表示し、リクエストの完了後に非表示にします。問題はないようです。効果を明確に確認するために、次のように p.php コードを 3 秒間スリープさせます:
<?php sleep(3); echo ("aaaaaa"); ?>
しかし、ボタンをクリックしたときに問題が発生し、読み込み中の画像が期待どおりに表示されず、ページがまったく応答しませんでした。長い間トラブルシューティングを行った結果、原因が async:false であることがわかりました。
ブラウザのレンダリング (UI) スレッドと js スレッドは相互に排他的であり、時間のかかる js 操作を実行すると、ページのレンダリングがブロックされます。非同期 ajax を実行する場合は問題ありませんが、同期リクエストに設定すると、他のアクション (ajax 関数の背後にあるコードとレンダリング スレッド) が停止します。 DOM 操作ステートメントがリクエストが開始される前の文である場合でも、この同期リクエストは実行時間を与えずに UI スレッドを「すぐに」ブロックします。これがコードが失敗する理由です。
setTimeout はブロックの問題を解決します
問題が何であるかを理解したので、解決策を見つけてみましょう。同期ajaxリクエストがスレッドをブロックしないようにするには、setTimeoutを考え、sestTimeoutにリクエストコードを入れて、ブラウザがスレッドを再起動して動作できるようにすれば問題は解決するのではないでしょうか?それ以来、私のコードは次のようになりました:
$(".btn2").click(function(){ $(".loadingicon").show(); setTimeout(function(){ $.ajax({ url : "p.php", async : false, success: function(data){ $(".loadingicon").hide(); alert(data); } }); }, 0); });
setTimeout の 2 番目のパラメータは 0 に設定され、ブラウザは設定された最小時間の後に実行します。何はともあれ、まずは実行して確認しましょう。
結果ロード画面が表示されますが! ! !画像が動かないのはなぜですか? それは明らかにアニメーション GIF です。この時点で、同期リクエストは遅延したものの、実行中に UI スレッドがブロックされるだろうとすぐに思いました。このブロッキングは非常に優れており、gif画像さえも動かず、静止画像のように見えます。
結論は明らかです。SetTimeout は症状を解決しますが、根本的な原因は解決しません。これは、同期リクエストを「わずかに」非同期にすることと同じであり、依然として同期の悪夢に陥り、スレッドがブロックされます。計画は失敗した。
遅延を使用する時期です
jQuery は、バージョン 1.5 以降に Deferred オブジェクトを導入しました。これは、非常に便利な汎用非同期メカニズムを提供します。詳細については、Ruan Yifeng 先生による記事 http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html を参照してください。
そこで、次のように Deferred オブジェクトを使用してコードを書き直しました。
function getData3(){ var defer = $.Deferred(); $.ajax({ url : "p.php", //async : false, success: function(data){ defer.resolve(data) } }); return defer.promise(); } $(".btn3").click(function(){ $(".loadingicon").show(); $.when(getData3()).done(function(data){ $(".loadingicon").hide(); alert(data); }); });
ajax リクエストの async:false を削除したことがわかります。これは、リクエストが再び非同期であることを意味します。成功関数内の次の文にも注意してください: defer.resolve(data) Deferred オブジェクトの replace メソッドは、任意の型のパラメーターを渡すことができます。このパラメータはdoneメソッドで取得できるため、非同期でリクエストしたデータをこの方法で返すことができます。
この時点で、問題は解決されました。遅延オブジェクトは非常に強力で便利なので、活用できます。
私のテスト コードはすべて次のとおりです。興味のある学生はそれらを使用してテストできます。
<button class="btn1">async:false</button> <button class="btn2">setTimeout</button> <button class="btn3">deferred</button> <img class="loadingicon" style="position:fixed;left:50%;top:50%;margin-left:-16px;margin-top:-16px;display:none;" src=http://www.update8.com/Web/Jquery/"loading2.gif" alt="正在加载" /> <script> function getData1(){ var result; $.ajax({ url : "p.php", async : false, success: function(data){ result = data; } }); return result; } $(".btn1").click(function(){ $(".loadingicon").show(); var data = getData1(); $(".loadingicon").hide(); alert(data); }); $(".btn2").click(function(){ $(".loadingicon").show(); setTimeout(function(){ $.ajax({ url : "p.php", async : false, success: function(data){ $(".loadingicon").hide(); alert(data); } }); }, 0); }); function getData3(){ var defer = $.Deferred(); $.ajax({ url : "p.php", //async : false, success: function(data){ defer.resolve(data) } }); return defer.promise(); } $(".btn3").click(function(){ $(".loadingicon").show(); $.when(getData3()).done(function(data){ $(".loadingicon").hide(); alert(data); }); });</script>
ps:$.ajax のパラメータの説明
パラメータの説明
URL が必要です。リクエストの送信先となる URL を指定します。
データはオプションです。マップまたは文字列値。リクエストとともにサーバーに送信されるデータを指定します。
success(data, textStatus, jqXHR) オプション。リクエストが成功したときに実行されるコールバック関数。
データ型
オプション。予想されるサーバー応答のデータ型を指定します。
デフォルトでインテリジェントな判断が行われます (xml、json、script、または html)。