tl;dr : コールバックがブロックしないという暗黙のルールはありません。
JavaScript イベント キュー
Javascript には、スレッドはありません (WebWorkers を除きますが、それは異なります)。これは、コードの一部がブロックされると、アプリケーション全体がブロックされることを意味します。コールバックに魔法はありません。コールバックはただの関数です:
function longLoop(cb) {
var i = 1000000;
while (i--) ;
cb();
}
function callback() {
console.log("Hello world");
}
function fun() {
longLoop(callback);
console.log("Called after Hello World is printed");
}
何かを非同期にするには、コールバックをイベント キューに入れる必要があります。これは、一部の API 呼び出しでのみ発生します。
- ユーザーが開始したイベント ハンドラー - クリック、キーボード、マウス
- API イベント ハンドラー - XmlHTTPRequest コールバック、WebWorker 通信
- タイミング関数 - setTimeout、setInterval
イベントがトリガーされると、コールバックはイベント キューに配置され、他のすべてのコールバックの実行が終了したときに実行されます。これは、コードの 2 行が同時に実行されることはないことを意味します。
先物
先物に関するこの投稿に少なくとも出くわしたと思います。そうでない場合、それは良い読み物であり、馬の口から直接出てきます.
Javascript では、非同期コードを理解するためにいくつかの作業を行う必要があります。非同期ループやシーケンスなどの一般的なことを行うための、futuresと呼ばれる Javascript ライブラリさえあります (完全な開示、私はこのライブラリの作成者と個人的に協力しました)。
未来の概念は、将来のある時点で未来が完成するという、未来を作成する機能によってなされる約束です。非同期呼び出しを行う場合は、future を作成し、それを返し、非同期呼び出しが終了したときに約束を果たします。ブログ投稿から:
Future<Results> costlyQuery() {
var completer = new Completer();
database.query("SELECT * FROM giant_table", (results) {
// when complete
completer.complete(results);
});
// this returns essentially immediately,
// before query is finished
return completer.future;
}
database.query がブロッキング コールの場合、future はすぐに完了します。database.query
この例では、非ブロッキング呼び出し (非同期) を想定しているため、この関数が終了した後にフューチャーが満たされます。指定された引数completer.complete
で渡された関数を呼び出します。completer.then()
あなたの例は、慣用的で非同期になるように変更されています:
void callback() {
print("callback called");
}
costlyQuery(sql) {
var completer = new Completer();
executeSql(sql, () => completer.complete());
return completer.future;
}
costlyQuery("select * from dual").then(callback);
これは非同期で、先物を正しく使用します。独自のコールバック関数を に渡し、コールcostlyQuery
バックでそれを呼び出してexecuteSql
同じことを達成することもできますが、それは Dart の方法ではなく、Javascript の方法です。
分離する
アイソレートは Java のスレッドに似ていますが、アイソレートは同時実行モデルであり、スレッドは並列モデルです (同時実行と並列処理の詳細については、この SO の質問を参照してください)。
Dart は、仕様がすべてを単一のスレッドで実行することを義務付けていないという点で特別です。異なる実行コンテキストが同じデータにアクセスできないようにするだけです。各アイソレートには独自のメモリがあり、送受信ポートを介して他のアイソレートとのみ通信 (データの送信など) できます。
これらのポートは、よく知られている場合、Go のチャネルと同様に機能します。
分離は、他のコードから独立して実行される分離された実行コンテキストです。Javascript の用語では、これは独自のイベント キューがあることを意味します。分離株のより完全な紹介については、Dart ツアーを参照してください。
executeSql がブロッキング関数であるとしましょう。終了するのを待ちたくない場合は、isolate にロードできます。
void callback() {
print("callback called");
}
void costlyQuery() {
port.receive((sql, reply) {
executeSql(sql);
reply.send();
});
}
main() {
var sendPort = spawnFunction(costlyQuery);
// .call does all the magic needed to communicate both directions
sendPort.call("select * from dual").then(callback);
print("Called before executeSql finishes");
}
このコードは、isolate を作成し、そこにデータを送信してから、完了時のコールバックを登録します。executeSql
ブロックしても、main()
必ずブロックするとは限りません。