非同期リクエストを行う関数foo
から応答/結果を返すにはどうすればよいですか?
コールバックから値を返し、その結果を関数内のローカル変数に代入してその変数を返そうとしましたが、これらのメソッドはいずれも実際には応答を返しません。すべてunknown
またはその他のメソッドを返します。その他の変数result
の初期値は次のとおりです。
コールバックを受け入れる非同期関数の例(jQuery のajax
関数を使用):
リーリー
Node.js を使用した例:
リーリー
then
プロミス ブロックを使用した例:
リーリー
コードで jQuery を使用しない場合は、この回答が役に立ちます
コードは次のようになります:リーリー
Felix Kling は、AJAX に jQuery を使用する人々向けに素晴らしい回答を書いてくれましたが、私は jQuery を使用しない人々向けに代替案を提供することにしました。(
新しいfetchAPI、Angular または Promise を使用している人のために、以下に別の回答を追加しました
あなたが直面している問題)
これは、別の回答の「質問の説明」の短い要約です。これを読んでもよくわからない場合は、その回答を読んでください。
AJAX の
Aは、asynchronousを表します。これは、リクエストの送信 (または応答の受信) が通常の実行フローから削除されることを意味します。あなたの例では、.send
これは簡単な例えです:はすぐに戻り、 code>success
コールバックreturn result; ## として渡した関数を呼び出す前に次のステートメントが実行されます。 #。
これは、戻ったときに、定義したリスナーがまだ実行されていないこと、つまり、返された値がまだ定義されていないことを意味します。
リーリー ######(バイオリン)######
a=5
部分はまだ実行されていないため、返されるa値はunknown
です。 AJAX は、サーバーがブラウザに値を伝える前に値を返すように動作します。
と呼ばれます。基本的に、完了時に実行するアクションをこの問題に対する考えられる解決策の 1 つは、計算完了後にプログラムに何を行うかを指示するコードを
リアクティブに記述することです。
リーリーこれは
CPSgetFiveに渡し、イベントが完了したときにどのように反応するかをコードに指示します (AJAX 呼び出し、この場合はタイムアウトなど)。
使用方法:リーリー「5」が画面に表示されます。 ###(バイオリン)###。###可能な解決策###
この問題を解決するには、基本的に 2 つの方法があります:
AJAX 呼び出しを同期にします (これを SJAX と呼びます)。
コールバックで適切に動作するようにコードをリファクタリングします。###1。同期 AJAX - やめてください。 !
同期 AJAX に関しては、
やめてください。Felix の答えは、これがなぜ悪い考えなのかについて、いくつかの説得力のある議論を示しています。全体として、サーバーが応答を返すまでユーザーのブラウザがフリーズし、非常に悪いユーザー エクスペリエンスが作成されます。その理由を説明する MDN の別の短い要約を次に示します。
これを行う必要がある場合は、フラグを渡すことができます。- 具体的な方法は以下の通りです
:
- リーリー ###2。組織再編コード
関数がコールバックを受け入れるようにします。コード例では、foo
がコールバックを受け入れるようにできます。fooが完了したときにreact
を行う方法をコードに伝えます。###それで:### リーリー ###なる:###リーリー
ここでは匿名関数を渡していますが、既存の関数への参照も同じように簡単に渡すことができ、次のようになります。 リーリー
このタイプのコールバック設計を実現する方法の詳細については、Felix の回答を確認してください。 次に、対応する操作を実行するために foo 自体を定義しましょうリーリー ######(バイオリン)######
これで、foo関数が、AJAX が正常に完了したときに実行するアクションを受け入れるようになりました。応答ステータスが 200 でないことを確認し、適切なアクション (失敗ハンドラーの作成など) を実行することで、この機能をさらに拡張できます。それは私たちの問題を効果的に解決しました。
これをまだ理解できない場合は、MDN の AJAX スタート ガイドを読んでください。
###質問###
AjaxのAはasynchronousを表します。これは、リクエストの送信 (または応答の受信) が通常の実行フローから削除されることを意味します。あなたの例では、$.ajaxはすぐに戻り、
success
コールバックとして渡す関数が呼び出される前に、次のステートメントreturn result;
が実行されます。これは、同期ストリームと非同期ストリームの違いをより明確にするための例えです:
同期
あなたが友人に電話して、何か情報を見つけてもらうように頼んだと想像してください。時間はかかるかもしれませんが、友人が必要な答えをくれるまで、電話のそばで宇宙を見つめながら待ちます。
「通常の」コードを含む関数呼び出しを行う場合にも、同じことが起こります。
リーリー findItemの実行には長い時間がかかる場合がありますが、
あなたはまた同じ理由で友達に電話をかけます。しかし、今回はあなたが不安なので、携帯電話var item = findItem();
の後のコードは、関数が結果を返すまで待機する必要があります。
非同期に折り返し電話してくださいと伝えます。電話を切り、家を出て、予定していたすべてのことを行います。友人があなたに折り返し電話をかけてきたら、あなたは彼があなたにくれた情報を処理していることになります。
これはまさに、Ajax リクエストを行うときに起こることです。リーリー応答を待たずに、すぐに実行を継続し、Ajax 呼び出しの後にステートメントを実行します。最終的に応答を取得するには、応答の受信後に呼び出される関数、コールバックを提供する必要があります (何かお気づきですか? コールバック?)。この呼び出しの後のステートメントは、コールバックが呼び出される前に実行されます。
###解決###
JavaScript の非同期の性質を活用してください。
一部の非同期操作は (「Ajax」のように) 同期操作を提供しますが、特にブラウザーのコンテキストでは、その使用は一般に推奨されません。なぜそれが悪いのですか?
JavaScript はブラウザの UI スレッドで実行され、長時間実行されるプロセスによって UI がロックされ、応答しなくなる可能性があります。また、JavaScriptの実行時間には上限があり、ブラウザは実行を継続するかどうかをユーザーに尋ねます。これらはすべて、非常に悪いユーザー エクスペリエンスにつながる可能性があります。ユーザーは、すべてが適切に動作しているかどうかを判断できません。さらに、インターネット速度が遅いユーザーの場合、影響はさらに大きくなります。
以下では、相互に構築される 3 つの異なるソリューションを紹介します。
async/await
との約束 (ES2017、トランスパイラーまたはリジェネレーターを使用している場合は古いブラウザーで動作します)
Promise と
async/await を使用する
ECMAScript の 2017 リリースでは、非同期関数の
構文レベルのサポートが導入されました。
を使用すると、「同期スタイル」で非同期に書くことができます。コードは依然として非同期ですが、読みやすく理解しやすくなっています。async
とawaitasync/await
Promise に基づいて構築:async
関数は常に Promise を返します。await
Promise を「ラップ解除」し、解決された Promise の値を生成するか、Promise が拒否された場合はエラーをスローします。IMPORTANT:
await
は、JavaScript モジュール。トップレベルのawait
はモジュールの外部ではサポートされていないため、asyncコンテキスト (モジュールを使用しない場合)。async
遅延と
awaitについて読むことができます。
これは、上記の関数findItem()の詳細を示す例です:
ブラウザリーリー
現在のおよびnodeバージョンはasync/awaitをサポートしています。
関数にコールバックを受け入れさせますregenerator
(または regenerator を使用するツール) を使用してコードを ES5 に変換し、Babelなどの古い環境をサポートすることもできます。コールバックは、関数 1 が関数 2 に渡されるときを指します。関数 2 は、準備ができたら関数 1 を呼び出すことができます。非同期プロセスのコンテキストでは、非同期プロセスが完了するたびにコールバックが呼び出されます。通常、結果はコールバックに渡されます。
質問の例では、
fooにコールバックを受け入れさせ、それを
success
コールバックとして使用できます。したがって、この### リーリー ###なりました### リーリーここでは「インライン」関数を定義しますが、任意の関数参照を渡すことができます:
リーリーfoo
自体は次のように定義されます:リーリー
callback
に渡した関数を参照し、それをは、呼び出し時に
foosuccess
に渡します。つまり。 Ajax リクエストが成功すると、
$.ajaxは
callbackを呼び出し、応答をコールバックに渡します (このように定義しているため、
resultで参照できます)コールバック)。
応答をコールバックに渡す前に処理することもできます。
リーリーコールバックを使用したコードの記述は、見た目よりも簡単です。結局のところ、ブラウザーの JavaScript は主にイベント駆動型 (DOM イベント) です。 Ajax 応答の受信は単なるイベントです。 サードパーティのコードを使用する必要がある場合は問題が発生する可能性がありますが、ほとんどの問題はアプリケーション フローを考えるだけで解決できます。
ES2015:then()的 Promise >
を使用Promise APIは ECMAScript 6 (ES2015) の新しい機能ですが、すでに優れたブラウザ サポートを備えています。標準の Promises API を実装し、非同期関数の使用と構成を簡素化するための追加メソッドを提供するライブラリも多数あります (例:Bluebird)。
Promise は、将来の値のコンテナです。 Promise が値を受け取るか (resolved)、またはキャンセルされると (rejected)、その値にアクセスしたいすべての「リスナー」に通知します。
通常のコールバックと比較した利点は、コードを分離でき、記述が容易であることです。
これは Promise の使用例です: