web-dev-qa-db-ja.com

すべてのJavaScriptコールバックは非同期ですか?そうでない場合、どのように私はどれであるかを知ることができますか?

すべてのjavascriptコールバックが非同期であるかどうか、または特定の状況でのみそうであるかどうかについて知りたいです。また、JavaScriptコードを非同期にする理由(または非同期JavaScriptの使用方法)がブラウザーとnodejsで異なることは確かなので、それぞれの状況で実際の非同期JavaScriptを構成するものを知りたいと思います。

次のシナリオでは、実際には非同期コードを記述していないという印象を受けます。

function addOne(value){
  value = value + 1;
  return value;
}

function simpleMap(values, callback){
  for(i = 0; i < values.length; i++){
    val = values[i];
    val = callback(val);
    values[i] = val;
  }
  return values;
}

newValues = simpleMap([1,2,3], addOne);

ただし、たとえば、jQueryのAJAX関数は本当に非同期です(現在利用可能なpromiseを考慮していません)。jQueryのAJAX非同期ですか?XHRリクエストを含むのは簡単ですか?ブラウザでは、すべてのXHRリクエストは非同期ですか?

Nodejs環境についても同じ質問があります。ノード内の何かが、ファイルI/O、process.nextTick、setTimeout、またはsetIntervalのようなものを含む場合にのみ非同期にできますか? mongodb/mongooseでデータベース呼び出しのようなことをするとき、なぜそれは非同期ですか?それを実現している舞台裏で何が起こっているのでしょうか?

非同期の「状況」は環境によって事前に決定されていますか?または、環境の非常に特定の機能(xhr、ノード内のファイルio、process.nexttickなど)を利用せずに、自分の関数を真に非同期にする方法はありますか?

34
jacheson

すべてのJavaScriptコールバックが非同期であるかどうかについて知りたい

いいえ。たとえば、Array#sortが使用するコールバックは非同期ではなく、String#replaceが使用するコールバックでもありません。

コールバックが非同期であるかどうかを知る唯一の方法は、そのドキュメントからです。通常、外部リソース(ajax呼び出しなど)の要求を伴うものは非同期であり、その他は非同期である場合とそうでない場合があります。

ただし、たとえば、jQueryのAJAX関数は本当に非同期です...

必ずしもそうとは限りません。現在、jQueryにはまだasyncフラグがあり、falseを設定して同期リクエストを強制できます。 (これは良い考えではなく、削除されますが、can。jQueryは、同期/非同期動作を提供する基になるブラウザーオブジェクトにフラグを渡します。)

JQueryのAJAX非同期にするのは何ですか?

ブラウザ。 jQueryのajax呼び出しは、XMLHttpRequestオブジェクト(または特定の状況では、script要素)を使用します。これは、デフォルトでブラウザーによって提供される非同期操作になります。

または、環境の非常に特定の機能を活用せずに、自分の機能を真に非同期にする方法はありますか...

最近まで、いいえ。第5版の仕様まで、JavaScript 言語は基本的にスレッドと非同期性の概念全体について沈黙していました。それが思いついたのは、あなたが環境に入ったときだけでした。何かを非同期にする唯一の方法は、NodeJSのnextTick(または非同期で完了するさまざまな操作のいずれか)やブラウザーのsetTimeoutなどのホスト提供の関数を使用することでした。

2015年6月のECMAScript第6版の仕様では、言語にpromisesが導入されました。 thenなどを介してES6promiseに接続されたコールバックは常に非同期で呼び出されるため(コールバックがアタッチされたときにPromiseがすでに解決されている場合でも)、JavaScriptは言語レベルになりました。したがって、コールバックを受け入れるのではなく、promiseを返すように関数を実装すると、それに接続されたthenコールバックが非同期でトリガーされることがわかります。

42
T.J. Crowder

独自の非同期関数を作成するには、インタプリタによって提供される可能性のある他の非同期関数を利用する必要があります。

たとえば、このコードは非同期の関数「addKeyHandler」を定義します。ただし、これが機能するのは、document.onKeyがJSエンジンによって非同期的に呼び出されるためです。 JavaScriptエンジンは、オペレーティングシステムが非同期機能を提供し、それがJSによって使用されるため、非同期機能を提供できます。 OSは、ハードウェアが非同期機能を提供するため(ハードウェア割り込みと呼ばれる)、非同期機能のみを提供できます。

ただし、OSとハードウェアが非同期機能を提供していなかった場合でも、JSインタープリターを作成することは可能です。ただし、無限ループを使用し、各反復でイベントが発生したかどうかを確認してから、適切なコールバックを呼び出す必要があります。つまり、CPUは常に全負荷状態になります。

var keyCallbacks = [];

var addKeyHandler = function(f) {
  keyCallbacks.Push(f);
};

document.onkeypress = function(e) {
  keyCallbacks.forEach(function(f) {
      f(e);
  });
};

addKeyHandler(function(e) {
    console.log(String.fromCharCode(e.charCode));
});
9
SpiderPig

自分で呼び出すコールバックは通常の関数呼び出しであり、常に同期しています。

特定のネイティブAPI(AJAX、geolocation、Node.jsディスクまたはネットワークAPIなど)は非同期であり、イベントループの後半でコールバックを実行します。

非同期コールバック内から同期的にコールバックを呼び出すと、それも非同期になります。

7
SLaks

コールバックを取得するだけでは、関数は非同期になりません。 ArrayforEachのように、関数の引数を取るが非同期ではない関数の例はたくさんあります。

関数を非同期にするには、非同期操作を実行する必要があります。非同期性を導入する方法は

  • タイマー関数setTimeoutsetInterval
  • 特殊関数nextTicksetImmediate
  • i/Oの実行(ネットワークのリッスン、データベースのクエリ、リソースからの読み取りまたは書き込み)
  • イベントの購読

記事によると "コールバックを取ると関数は非同期になりますか?"

0
pspi