シングルスレッドの非同期操作を達成するためにどのようにJavascriptを

ルアンYifengの先生より転載:JavaScriptの詳細な動作機構:ループトークイベントhttp://www.ruanyifeng.com/blog/2014/10/event-loop.html

1.なぜJavaScriptがシングルスレッドでありますか?

JavaScript言語の大きな特徴は、同じ時間だけ一つのことを行うことができます、と言うことですつまり、シングルスレッドです。なぜJavaScriptを複数のスレッドが行う必要はありませ?これは、ああの効率を向上させることができます。

JavaScriptがシングルスレッドであり、その関連する目的。ブラウザのスクリプト言語としては、JavaScriptの主な目的は、同様に、ユーザと対話することです操作DOMこれは、それが唯一のシングルスレッド、それ以外の場合は非常に複雑な同期の問題をもたらすことができると判断しました。2つのスレッドが存在するが、例えば、DOMノード上のコンテンツを追加するための1つのスレッドJavaScriptを仮定し、別のスレッドがこのノードを削除するには、ブラウザどのスレッドが優先すべきか?

だから、生まれてから、煩雑さを避けるために、JavaScriptは、この言語の中心的な特徴となっていた、シングルスレッドで変更されることはありません。

、マルチコアCPUの演算能力を利用するにHTML5提出Web Worker标准はJavaScriptスクリプトはしかし、複数のスレッドを作成することができます子线程完全受主线程控制,且不得操作DOMだから、新しい標準は、シングルスレッドのJavaScriptの性質を変更しません。

2.タスクキュー

シングルスレッドの手段は、すべてのタスクは、ミッションの終了前に、後にタスクを実行し、ラインアップする必要があります。タスクは長い時間前にかかる場合は、その後のタスクが待っていました。

キューがあるため、計算集約、CPUビジー状態の場合、彼らはうまくやってますが、(Ajaxの操作はネットワークからデータを読み込むような)IOデバイス(入出力デバイス)が非常に遅いので、何度もCPUは、アイドル状態で、持っていました結果が出て、その後、ダウン実行を待っています。

設計者は、その後、メインスレッドは関係なく、IOデバイスの、待ちタスクでハングは、背面のタスクを実行することができ、JavaScript言語を実現します。IOデバイスに戻り、結果が戻ってくるまで、保留中のタスクが実行を継続するために待機します。

したがって、すべてのタスクは、2つに分けることができ、一方は同期タスク(同期)であり、他方は非同期タスク(非同期)です。メインスレッドのタスクの実行のためにキューイング同期タスク手段は、最初のタスクが実行された後のタスクに終了し、非同期タスクが意味する、のメインスレッドを入力し、「タスクキュー」(タスクキュー)を入力しないでくださいタスク、唯一の「タスクキュー」メインスレッドに通知し、非同期タスクを行うことができ、タスクは、メインスレッドを入力します。

具体的には、非同期実行の操作機構は、次の通りです。(同期実行は、あまりにも、それは、非同期実行非同期タスクとみなすことができないため。)

(1)すべての同期タスクは、スタックの実行(実行コンテキストスタック)を形成するメインスレッドで実行されます。

また、(2)メインスレッドでは、「タスクキュー」(タスクキュー)があります。非同期タスクの結果限り実行すると、イベントは自宅で「タスクキュー」に置かれています。

(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列"がどのようなイベントを参照するには。実行スタックにこれ待ち状態を終了する非同期タスクを、対応するものを、開始しました。

(4)メインスレッドは、上記の第3工程を繰り返します。

図は、メインスレッドとタスクキューの模式図です。
ここに画像を挿入説明
長い空のメインスレッドのように、それはJavaScriptを運用メカニズムである、読み「タスクキュー」を移動します。このプロセスが繰り返されます。

3.イベントとコールバック

「タスクキュー」は、タスクを完了するためにイベントキュー(待ち行列は、メッセージとして理解することができる)、IOデバイスである、あなたはそれで「タスクキュー」でイベントを追加関連する非同期タスクは「実行スタック」と入力していることを示しています。メインスレッドがイベントを読み取ることで、「タスクキュー」を、読み込みます。

イベントの「タスクキュー」は、イベントIOデバイスに加えて、(例えばマウスクリック、ページのスクロールなど)ユーザ生成イベントの数を含みます。ただ、これらのイベントが発生すると読んでメインスレッドを待ち、「タスクキュー」を入力しますが、あまりにもコールバック関数を指定します。

所谓"回调函数"(callback),就是那些会被主线程挂起来的代码メインスレッドが非同期タスクは、対応するコールバック関数を実行することで、実行を開始するときに、非同期タスクは、コールバック関数を指定する必要があります。

「タスクキュー」である先进先出データ構造、イベントの上面、優先順位がメインスレッドを読んであります。読み取りプロセスのメインスレッドは、空のスタックの実装として、最初のイベントの「タスクキュー」一つは自動的にメインスレッドに入る限り、本質的に自動です。しかし、後述する「タイマー」機能の存在に起因します主线程首先要检查一下执行时间,某些事件只有到了规定的时间,才能返回主线程

4.イベントループ

メインスレッドがイベントに「タスクキュー」から読み出し、プロセスは常にので、この機構の全体の動作は、イベントループ(イベントループ)として知られ、循環しています。

より良いイベントループを理解するには、以下を参照してください(フィリップ・ロバーツの演説から引用し、「ヘルプ、私はイベントループでこだわっています」)。
ここに画像を挿入説明

上の図は、メインスレッドの実行、ヒープ(ヒープ)とスタック(スタック)を製造するには、コードのスタックは、様々な外部のAPIを呼び出すと、彼らはの「タスクキュー」でさまざまなイベント(クリック、負荷が行われる)に追加されます。限りコードのスタックが完了すると、コールバック関数に対応し、それらのイベントの実施に続いて、「タスクキューを」、読んでますメインスレッドにアクセスしてください。

执行栈中的代码(同步任务),总是在读取"任务队列"(异步任务)之前执行次の例を考えてみましょう。

    var req = new XMLHttpRequest();
    req.open('GET', url);    
    req.onload = function (){};    
    req.onerror = function (){};    
    req.send();

上記のコードは、req.sendアヤックスを動作させることである、それはシステムが読みになり、非同期タスク、唯一の現在のスクリプトが実行されたすべてのコード手段であり、サーバーにデータを送信し、「タスクキューを。」したがって、それは以下の文言と同等です。

    var req = new XMLHttpRequest();
    req.open('GET', url);
    req.send();
    req.onload = function (){};    
    req.onerror = function (){};   

彼らは実行スタックの一部であるため、send()メソッドがない問題で、彼らは常に、完成実行されない前または後であることを、部分(のonloadとONERRORは)唯一の「タスクキューを読み取るために、コールバック関数を指定します。 "

5.タイマー

非同期イベントタスクを置くことに加えて、「タスクキュー」もコードの後、時間のある特定の量が実行されていることを、時限イベントを配置することができます。これは、コード実行のタイミングである「タイマー」(タイマー)機能、と呼ばれています。

タイマー機能は主によるsetTimeout()setInterval()、それは一回を繰り返したコードの実行を、指定されていることを除いて、まったく同じこの2つの機能、その内部運用メカニズムを完了すること。以下の議論したsetTimeout()。

setTimeout()は、第二の遅延実行までのミリ秒数であり、2つのパラメータ最初のコールバック関数を受け付けます。

// setTimeout(cb, time);
console.log(1);
setTimeout(function(){console.log(2);},1000);
console.log(3);
上面代码的执行结果是1,3,2,因为setTimeout()将第二行推迟到1000毫秒之后执行。

第二パラメータのsetTimeout()は0に設定されている場合、それは直ちに実行された後(0ミリ秒間隔)現在のコードの実行完了(空の実行スタック)を示すコールバック関数を指定。

setTimeout(function(){console.log(1);}, 0);
console.log(2);

唯一の2行目の実装が完了した後、システムはコールバック関数の「タスクキュー」を実行するために移動するので、上記のコードの結果は、常に2,1です。

要するに、setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行つまり、可能な限り早期に実装する必要があります。それはそう、「タスクキュー」の最後にイベントを追加します要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行

HTML5标准规定了setTimeout()的第二个参数的最小值(最短间隔),不得低于4毫秒、この値未満は自動的に増加します場合。これに先立ち、ブラウザの旧バージョンでは、10ミリ秒までの最短間隔となります。加えて、对于那些DOM的变动(尤其是涉及页面重新渲染的部分),通常不会立即执行,而是每16毫秒执行一次そして、requestAnimationFrameの()のsetTimeout(より優れている)を使用します。

なお、「タスクキュー」へのイベントことを除いてはsetTimeout()は、現在のコード(実行スタック)が実行されるまで待つ必要があり、メインスレッドは、その指定されたコールバック関数を実行するために行くだろう。現在のコードに時間がかかる場合、それは長い時間を待たなければならないこと、およびsetTimeoutメソッドで指定された()の実行時間になりますコールバック関数を保証する方法はありません。

6. Node.jsの的イベントループ

Node.jsのは、シングルスレッドのイベントループですが、そのメカニズムは、ブラウザ環境を実行しているとは異なります。

次の図(著者@BusyRich)を見てください。
ここに画像を挿入説明

図によれば、Node.jsの操作機構は次のようです。

(1)V8エンジンは、JavaScriptのスクリプトを解析します。

(2)コードを解析した後、ノードAPIを呼び出します。

(3)libuvライブラリは、Node APIの実装を担当しています。これは、V8エンジンにバック非同期タスクの実行結果では、イベントループ(イベントループ)を形成するために、異なるスレッドに異なるタスクを割り当てます。

(4)V8エンジンは、結果がユーザに返さ。

setTimeoutとのsetIntervalこれらの2つの方法に加えて、Node.jsのは、2つの追加の「タスクキュー」関連のメソッドを備えている:process.nextTicksetImmediate彼らは、「タスクキュー」であるの理解を深めて私たちを助けることができます。

イベントループ現在の「実行スタック」テール---- ----トリガーコールバック関数の前に(「タスクキュー」を読み出すメインスレッド)で一度process.nextTickの方法。他に它指定的任务总是发生在所有异步任务之前言葉

setImmediate方法は、すなわち、現在の「タスクキュー」イベントの最後に追加さ它指定的任务总是在下一次Event Loop时执行れたsetTimeout(FN、0)のように、。(StackOverflowの経由)以下の例を考えてみましょう。

process.nextTick(function A() {
  console.log(1);
  process.nextTick(function B(){console.log(2);});
});

setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0)
// 1
// 2
// TIMEOUT FIRED

上記のコードは、process.nextTickメソッド指定されたコールバック関数ので、常に「実行スタック、」現在のトリガーの終わりに、それだけでなく、実行するために指定のsetTimeoutコールバック関数タイムアウトよりAの関数であるだけでなく、機能B比タイムアウトを実行します。この手段、複数process.nextTick文(彼らはネストされているか否か)、現在の「実行スタック」の実装では、すべてがある場合。

さて、setImmediateを見て。

setImmediate(function A() {
  console.log(1);
  setImmediate(function B(){console.log(2);});
});

setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0);

上記コードのsetTimeout(FN、0)とsetImmediate各添加コールバック関数A及びタイムアウト、次のイベントループがトリガされます。だから、コールバック関数、それを強制するには?答えは不明です。演算結果は、1-TIMEOUTは、-2を焼成してもよいまた、タイムアウトが焼成1--2をすることができます。

紛らわしいことに、Node.jsの文書はsetImmediateは常に先のsetTimeoutの、コールバック関数を指定し、言いました。実際には、これは場合にのみ、再帰呼び出しが起こります。

setImmediate(function (){
  setImmediate(function A() {
    console.log(1);
    setImmediate(function B(){console.log(2);});
  });

  setTimeout(function timeout() {
    console.log('TIMEOUT FIRED');
  }, 0);
});
// 1
// TIMEOUT FIRED
// 2

setImmediateで囲まれた上記のコード、およびsetImmediateのsetTimeoutは、その動作結果は常に1-TIMEOUTは、焼成2を、今回のトリガ機能の前に一定のタイムアウト。その次のラウンドイベントループにsetImmediateは常にイベント登録するので、バックTIMEOUTで2に関しては、(タイムアウト背後すなわち、関数Bトリガ)を解雇函数A和timeout是在同一轮Loop执行しながら函数B在下一轮Loop执行

よりprocess.nextTick文が実行されたら、それはあなたが複数のループ仕上げを行う必要があり、よりsetImmediateかもしれ常に現在の「実行スタック」です:私たちは、このように重要な違いprocess.nextTickとsetImmediateを取得します。実際には、これが理由ではNode.jsのバージョン10.0追加setImmediate方法で、または再帰呼び出しprocess.nextTickを以下のように、無限になり、メインスレッドは、「イベントキュー」を読んで行くことはありません!

process.nextTick(function foo() {
  process.nextTick(foo);
});

あなたは再帰的なprocess.nextTickを書く場合は実際には、Node.jsのはsetImmediateを変更するように求め、警告がスローされます。

また、process.nextTickは、コールバック関数が、この「イベントループ」トリガーで指定され、前者が常に以前後者、および効率より発生することは明らかであるようsetImmediateは、次の「イベントループ」トリガーで指定されているため高すぎる(何のチェック「タスクキュー」は存在しないため)。

148元記事公開 ウォンの賞賛136 ビューに25万+を

おすすめ

転載: blog.csdn.net/DlMmU/article/details/104685241