ファブリシオの答えはその場にあります。しかし私は、非同期性の概念を説明するのに役立つアナロジーに焦点を当てた、あまり技術的でないもので彼の答えを補足したいと思いました。
アナロジー...
昨日、私が行っていた作業には、同僚からの情報が必要でした。私は彼に電話をかけた。会話の流れは次のとおりです。
私:こんにちは、ボブ。先週、私たちがどのようにバーをfooしたかを知る必要があります。ジムはそれについての報告を望んでいます、そしてあなたはそれについての詳細を知っている唯一の人です。
Bob:もちろん。でも30分くらいかかる?
私:それは素晴らしいボブです。情報を入手したら、リングを返してください。
この時点で、電話を切りました。レポートを完成させるためにボブからの情報が必要だったので、レポートを離れて代わりにコーヒーを飲み、それから私はいくつかの電子メールに追いつきました。40分後(ボブは遅い)、ボブが電話をかけ直し、必要な情報をくれました。この時点で、必要な情報がすべて揃っていたので、レポートを使用して作業を再開しました。
代わりに、会話がこのようになったと想像してください。
私:こんにちは、ボブ。先週、私たちがどのようにバーをfooしたかを知る必要があります。ジムはそれについての報告を望んでおり、それについての詳細を知っているのはあなただけです。
Bob:もちろん。でも30分くらいかかる?
私:それは素晴らしいボブです。待ちます。
そして私はそこに座って待った。そして待った。そして待った。40分間。待っているだけです。最終的に、ボブから情報が提供され、電話を切り、レポートを完成させました。しかし、私は40分の生産性を失っていました。
これは非同期動作と同期動作です
これが、質問のすべての例でまさに起こっていることです。画像の読み込み、ディスクからのファイルの読み込み、およびAJAXを介したページの要求は、すべて(最近のコンピューティングのコンテキストでは)遅い操作です。
JavaScriptでは、これらの遅い操作が完了するのを待つのではなく、遅い操作が完了したときに実行されるコールバック関数を登録できます。その間、JavaScriptは引き続き他のコードを実行します。遅い操作が完了するのを待つ間、JavaScriptが他のコードを実行するという事実は、動作を非同期にします。JavaScriptが他のコードを実行する前に操作が完了するのを待っていたら、これは同期的な動作でした。
var outerScopeVar;
var img = document.createElement('img');
// Here we register the callback function.
img.onload = function() {
// Code within this function will be executed once the image has loaded.
outerScopeVar = this.width;
};
// But, while the image is loading, JavaScript continues executing, and
// processes the following lines of JavaScript.
img.src = 'lolcat.png';
alert(outerScopeVar);
上記のコードでは、負荷にJavaScriptを求めているlolcat.png
である、sloooowオペレーションです。この遅い操作が完了すると、コールバック関数が実行されますが、その間、JavaScriptは次のコード行を処理し続けます。すなわちalert(outerScopeVar)
。
これが、アラートが表示される理由undefined
です。以来alert()
すぐに処理されるのではなく、イメージがロードされた後。
コードを修正するには、alert(outerScopeVar)
コードをコールバック関数に移動するだけです。この結果、outerScopeVar
変数をグローバル変数として宣言する必要がなくなりました。
var img = document.createElement('img');
img.onload = function() {
var localScopeVar = this.width;
alert(localScopeVar);
};
img.src = 'lolcat.png';
コールバックが関数として指定されていることが常にわかります。これは、JavaScriptでコードを定義する唯一の*方法ですが、後で実行するためです。
したがって、すべての例で、これfunction() { /* Do something */ }
はコールバックです。すべての例を修正するには、操作の応答を必要とするコードをそこに移動するだけです!
*技術的にはeval()
同様に使用できますが、この目的のためにeval()
悪です
発信者を待たせるにはどうすればよいですか?
現在、これに似たコードがあるかもしれません。
function getWidthOfImage(src) {
var outerScopeVar;
var img = document.createElement('img');
img.onload = function() {
outerScopeVar = this.width;
};
img.src = src;
return outerScopeVar;
}
var width = getWidthOfImage('lolcat.png');
alert(width);
しかし、私たちは今、そのreturn outerScopeVar
ことがすぐに起こることを知っています。onload
コールバック関数が変数を更新する前。これにより、にgetWidthOfImage()
戻りundefined
、undefined
警告が表示されます。
これを修正するには、関数呼び出しgetWidthOfImage()
がコールバックを登録できるようにし、幅のアラートをそのコールバック内に移動する必要があります。
function getWidthOfImage(src, cb) {
var img = document.createElement('img');
img.onload = function() {
cb(this.width);
};
img.src = src;
}
getWidthOfImage('lolcat.png', function (width) {
alert(width);
});
...前と同じように、グローバル変数(この場合はwidth
)を削除できることに注意してください。