関数内で変更した後も変数は変更されませんか? - 非同期コードリファレンス
P粉403804844
2023-08-28 11:46:28
<p>以下の例では、<code>outerScopeVar</code>すべての状況下ですべて未定です?</p>
<pre class="brush:php;toolbar:false;">var innerScopeVar;
var img = document.createElement('img');
img.onload = function() {
externalScopeVar = this.width;
};
img.src = 'lolcat.png';
alert(outerScopeVar);</pre>
<pre class="brush:php;toolbar:false;">var innerScopeVar;
setTimeout(関数() {
externalScopeVar = 'Hello Asynchronous World!';
}, 0);
alert(outerScopeVar);</pre>
<pre class="brush:php;toolbar:false;">// jQuery を使用した例
var externalScopeVar;
$.post('loldog', function(response) {
externalScopeVar = 応答;
});
alert(outerScopeVar);</pre>
<pre class="brush:php;toolbar:false;">// Node.js の例
var externalScopeVar;
fs.readFile('./catdog.html', function(err, data) {
externalScopeVar = データ;
});
console.log(outerScopeVar);</pre>
<pre class="brush:php;toolbar:false;">// 約束付き
var externalScopeVar;
myPromise.then(関数 (応答) {
externalScopeVar = 応答;
});
console.log(outerScopeVar);</pre>
<pre class="brush:php;toolbar:false;">// オブザーバブルを使用
var externalScopeVar;
myObservable.subscribe(関数 (値) {
externalScopeVar = 値;
});
console.log(outerScopeVar);</pre>
<pre class="brush:php;toolbar:false;">// 地理位置情報 API
var externalScopeVar;
navigator.geolocation.getCurrentPosition(function (pos) {
外側スコープ変数 = pos;
});
console.log(outerScopeVar);</pre>
<p> これらのすべての例で、<code>未定義</code>が出力されます。 </p> 解決方法は必要ありませんが、私たちはこのような事態が発生することを知っています。</p>
<ブロック引用>
<p><strong>注意:</strong>これは JavaScript の性質に関する問題であり、今後、この問題が修正され、さらに多くの許容される例が追加されます。
</blockquote><p><br /></p>
Fabrício の答えは非常に正しいですが、私は、アナロジーを通じて非同期性の概念を説明することに重点を置き、少し専門的ではない内容で彼の答えを補足したいと思います。
###類推...###昨日、私は同僚から情報を得る必要がある仕事をしていました。彼に電話したところ、会話は次のようになりました:
会話が次のようになったと想像してください;
私はそこに座って待っていました。そして待った。そして待った。 40分。何もせずただ待ってください。結局、ボブが私に情報を提供し、電話を切り、私は報告を終えました。しかし、生産性は 40 分失われました。
これは非同期動作と同期動作の比較です
これはまさに、私たちの質問のすべての例で起こっていることです。画像の読み込み、ディスクからのファイルの読み込み、AJAX 経由でのページのリクエストはすべて (最新のコンピューティングのコンテキストでは) 遅い操作です。
これらの遅い操作が完了するまで待機するのではなく、遅い操作が完了したときに実行されるコールバック関数を登録できます。ただし、その間、JavaScript は他のコードの実行を続けます。遅い操作が完了するのを待っている間に JavaScript が
other codeを実行しているという事実により、動作は 非同期になります。 JavaScript が操作の完了を待ってから他のコードを実行する場合、これは 同期 動作になります。 リーリー 上記のコードでは、JavaScript に lolcat.png をロードするように依頼しています。これは
sloooow操作です。この遅い操作が完了すると、コールバック関数が実行されますが、その間、JavaScript はコードの次の行、つまり
alert()alert(outerScopeVar)
の処理を続けます。 これが、unknown
を示すアラートが表示される理由です。これは、がイメージのロード後ではなく、すぐに処理されるためです。
コードを修正するには、
outerScopeVaralert(outerScopeVar)
コードをコールバック関数に移動するだけです。したがって、変数をグローバル変数として宣言する必要はなくなりました。
リーリー
コールバックは関数として指定されていることが常に表示されます。これは、JavaScript でコードを定義して後で実行する唯一の方法だからです。
つまり、すべての例で、 function() { /* Do Something */ } はコールバックです。すべての 例を修正するには、必須 レスポンスを操作するコードはそこに移動されます。
#* 技術的には
はその目的にとっては悪ですeval()
も使用できますが、eval()
発信者を保留にするにはどうすればよいですか?
現在、これに似たコードがある可能性があります; リーリー ただし、
onloadコールバック関数が変数を更新する前に、outerScopeVar
を返す
が即座に行われることがわかりました。これにより、getWidthOfImage()
はunknown
を返し、アラート
unknownを生成します。
これを修正するには、
...前と同様に、グローバル変数 (この場合はgetWidthOfImage()
を呼び出す関数がコールバックを登録し、そのコールバック内に幅アラートを移動できるようにする必要があります。 リーリーwidth
) を削除できたことに注目してください。
一言で答えます: 非同期性。
###序文###このトピックは、Stack Overflow で少なくとも何千回も繰り返されています。そこで最初に、非常に役立つリソースをいくつか紹介したいと思います。
。同期プロセスと非同期プロセスを説明した彼の優れた回答と、「コードの再編成」セクションを参照してください。 @Benjamin Gruenbaum は、同じスレッド内の非同期性の説明にも多大な労力を費やしました。
も、非同期性を簡単な方法で説明するのに適しています。
まず、一般的な動作を追跡しましょう。すべての例で、
outerScopeVarは
function
内で変更されます。この関数は明らかにすぐには実行されず、割り当てられるかパラメータとして渡されます。これを コールバック と呼びます。 ここでの問題は、このコールバックがいつ呼び出されるのかということです。それは特定の状況によって異なります。もう一度、一般的な動作を追跡してみましょう:
img.onload- (画像が正常に読み込まれた場合)
setTimeout- は、遅延が期限切れになり、タイムアウトが
jQuery のコールバック - $.post が呼び出される可能性があります。
は、- 将来、ファイルが正常に読み取られたか、エラーがスローされたときに
将来のある時点で実行される可能性のあるコールバックがあります。この「将来のいつか」を
非同期ストリーム将来のある時点で
と呼ばれる可能性があります。clearTimeout
キャンセルされなかった後、将来の時点で呼び出される可能性があります。注: 遅延として
0 を使用する場合でも、すべてのブラウザには最小タイムアウト遅延の上限があります (HTML5 仕様では 4 ミリ秒と指定されています)。Ajax リクエストが正常に完了すると、
将来のある時点でNode.js の
fs.readFile呼び出される可能性があります。
すべての場合において、と呼びます。 非同期実行は同期プロセスからプッシュされます。つまり、同期コード スタックの実行中、非同期コードは 永遠に 実行されます。これが JavaScript のシングルスレッドの特徴です。
より具体的には、JS エンジンがアイドル状態であるとき (大量の同期コードを実行していないとき) は、非同期コールバックをトリガーする可能性のあるイベント (タイムアウト、ネットワーク応答の受信など) をポーリングし、それらを次々に実行します。これは イベント ループ とみなされます。
つまり、赤い手書きの形で強調表示された非同期コードは、それぞれのコード ブロック内の残りの同期コードがすべて実行された後でのみ実行できます。
つまり、コールバック関数は同期的に作成されますが、実行は非同期的に行われます。非同期関数が実行されたことがわかるまでは、その実行に依存することはできません。どうすればよいでしょうか?alert
とconsole.log
を移動すると、その時点で結果が利用できるため、期待どおりの結果が出力されます。独自のコールバック ロジックを実装する
多くの場合、非同期関数が呼び出される場所に応じて、非同期関数の結果に対してさらに多くの操作を実行したり、結果に対して異なる操作を実行したりする必要があります。より複雑な例を見てみましょう:
リーリー注: 私は、汎用の非同期関数としてランダムな遅延を伴う
setTimeout
を使用しています。同じ例は、Ajax、readFile、onload、およびその他の非同期フローでも機能します。この例には明らかに他の例と同じ問題があり、async 関数が実行されるまで待機しません。
独自のコールバック システムを実装して、この問題を解決しましょう。まず、この場合はまったく役に立たない醜い
リーリーouterScopeVar
を削除します。次に、関数の引数を受け入れるパラメーター、つまりコールバックを追加します。非同期操作が完了すると、このコールバックを呼び出して結果を渡します。実装 (コメントを順番に読んでください):上記の例のコード スニペット: