ホームページ  >  記事  >  ウェブフロントエンド  >  古典的な手法: シングルスレッドと非同期 JavaScript

古典的な手法: シングルスレッドと非同期 JavaScript

WBOY
WBOY転載
2022-02-03 07:00:302034ブラウズ

この記事では、JavaScript のシングルスレッドと非同期に関する関連知識を提供します。お役に立てば幸いです。

古典的な手法: シングルスレッドと非同期 JavaScript

この記事を書くにあたり、私もたくさんの記事を読みましたが、ほとんどが非常に単純で、概念的なことは非常に曖昧だったので、いくつかのコースを聞いて探しました将来のレビューのためにここに簡単にまとめておきます~

プロセスとスレッド

1. プロセス : プログラムが完成したら実行されると、ユニークメモリ空間を占有します ---- Windows タスク マネージャを通じてプロセスを表示できます;

##2. スレッド: これはプロセス内の 独立した 実行単位であり、プログラム実行の完全なプロセスであり、CPU の基本的なスケジューリング単位です。

3. プロセスとスレッドの関係:

* 通常、プロセス内には少なくとも 1 つの実行中のスレッドがあります:

メイン スレッド -- プロセスが終了した後開始 自動的に作成;

* 1 つのプロセスで複数のスレッドを同時に実行することもできます。プログラムは

マルチスレッド;

* データプロセス内では、複数のスレッドで直接共有できます。

* 複数のプロセス間でデータを直接共有することはできません

4. ブラウザは単一プロセスを実行していますか?

* 一部は単一プロセスです

* firefox

* 一部は複数プロセスです

* chrome

5.ブラウザはマルチプロセスです どのプロセスが実行されていますか?

* タスク マネージャ==>プロセス

6. ブラウザです。シングルスレッドで実行されていますか? マルチスレッドですか?

* これらはすべてマルチスレッドで実行されます


シングルスレッド

1. シングルスレッドとは

JavaScript 言語の主要な機能はシングルスレッドであり、これは同時に 1 つのことしか実行できないことを意味します。

//栗子
console.log(1)
console.log(2)
console.log(3)
//输出顺序 1 2 3
2. JavaScript がシングルスレッドである理由

    まず第一に、歴史的な理由があります。JavaScript 言語が作成されたとき、マルチプロセスおよびマルチスレッド アーキテクチャは存在しませんでした。人気があり、ハードウェアのサポートが利用できませんでした。良くありません。
  • 第 2 に、マルチスレッドの複雑さのため、マルチスレッド操作にはロックが必要となり、コーディングの複雑さが増加します。
  • 最後に目的についてですが、ブラウザスクリプト言語としてのJavaScriptの主な目的は、ユーザーと対話してDOMを操作することです。 DOM レンダリングの結果は予測できません。
マルチコア CPU の計算能力を活用するために、HTML5 は Web Worker 標準を提案しています。これにより、JavaScript スクリプトは複数のスレッドを作成できますが、子スレッドはメインスレッドによって完全に制御されます。スレッドを実行する必要があり、DOM を操作してはなりません。したがって、この新しい標準は JavaScript のシングルスレッドの性質を変更しません。


同期と非同期

1. JS 同期タスク/非同期タスク

同期タスク: in メインスレッド上 実行のためにキューに入れられたタスクは、 最初の タスクが完了した後にのみ実行できます。 すべての同期タスクはメインスレッドで実行されます。 実行スタック

(実行コンテキスト スタック) を形成します。

非同期タスク:

タスクは

メイン スレッドの外側で 実行されます。メイン スレッドの外側には、「タスク キュー」(タスク キュー) もあります。 )、非同期タスクが完了すると、待機するコールバック関数の形式で タスク キュー に入れられます。メイン スレッドがアイドル状態になると、メイン スレッドはイベント キューに移動して待機します。待機中のコールバック関数を取り出してメインスレッドに入れて実行します。この処理を繰り返し実行することで、jsのイベントループ機構(イベントループ)が形成されます。

//栗子
// 同步
console.log(1)

// 异步
setTimeout(()=>{
    console.log(2)
},100)

// 同步
console.log(3)

//输出顺序 1 3 2
2. JavaScript が非同期である必要がある理由JS コードの実行中に、特定のコード部分が長時間実行されると、後続のコードは長時間実行されなくなります。その結果、

ブロッキング

(つまり、スタック) が発生し、ユーザー エクスペリエンスに影響を及ぼします。

3. 非同期 JavaScript の実装方法

1) 実行スタックとタスク キュー

実際、JS は

イベント ループを通じて非同期を実装することはすでに上で述べました。

;

最初にいくつかの概念を理解しましょう:

  • JS任务 分为同步任务(synchronous)和异步任务(asynchronous)
  • 同步任务都在 JS引擎线程(主线程) 上执行,形成一个执行栈(call stack)
  • 事件触发线程 管理一个 任务队列(Task Queue)
  • 异步任务 触发条件达成,将 回调事件 放到任务队列(Task Queue)中
  • 执行栈中所有同步任务执行完毕,此时JS引擎线程空闲,系统会读取任务队列,将可运行的异步任务回调事件添加到执行栈中,开始执行

 当一个JS文件第一次执行的时候,js引擎会 解析这段代码,并将其中的同步代码 按照执行顺序加入执行栈中,然后从头开始执行。如果当前执行的是一个方法,那么js会向执行栈中添加这个方法的执行环境,然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。这个过程反复进行,直到执行栈中的代码全部执行完毕。

栗子 

//(1)
console.log(1)

//(2)
setTimeout(()=>{
    console.log(2)
},100)

//(3)
console.log(3)
  1. 先解析整段代码,按照顺序加入到执行栈中,从头开始执行
  2. 先执行(1),是同步的,所以直接打印 1
  3. 执行(2),发现是 setTimeout,于是调用浏览器的方法(webApi)执行,在 100ms后将 console.log(2) 加入到任务队列
  4. 执行(3),同步的,直接打印 3
  5. 执行栈已经清空了,现在检查任务队列,(执行太快的话可能此时任务队列还是空的,没到100ms,还没有将(2)的打印加到任务队列,于是不停的检测,直到队列中有任务),发现有 console.log(2),于是添加到执行栈,执行console.log(2),同步代码,直接打印 2 (如果这里是异步任务,同样会再走一遍循环:-->任务队列->执行栈)

所以结果是 1 3 2;

注意:setTimeout/Promise等我们称之为任务源。而进入任务队列的是他们指定的回调;

2)宏任务(macro task)与微任务(micro task)

 上面的循环只是一个宏观的表述,实际上异步任务之间也是有不同的,分为 宏任务(macro task) 与 微任务(micro task),最新的标准中,他们被称为 task与 jobs

  • 宏任务有哪些:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering(渲染)
  • 微任务有哪些:process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)

下面我们再详细讲解一下执行过程:

 执行栈在执行的时候,会把宏任务放在一个宏任务的任务队列,把微任务放在一个微任务的任务队列,在当前执行栈为空的时候,主线程会 查看微任务队列是否有事件存在。如果微任务队列不存在,那么会去宏任务队列中 取出一个任务 加入当前执行栈;如果微任务队列存在,则会依次执行微任务队列中的所有任务,直到微任务队列为空(同样,是吧队列中的事件加到执行栈执行),然后去宏任务队列中取出最前面的一个事件加入当前执行栈...如此反复,进入循环。

注意:

  • 宏任务和微任务的任务队列都可以有多个
  • 当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。
  • 不同的运行环境 循环策略可能有不同,这里探讨chrome、node环境

 栗子

//(1)
setTimeout(()=>{
    console.log(1)   // 宏任务
},100)

//(2)
setTimeout(()=>{
    console.log(2)  // 宏任务
},100)

//(3)
new Promise(function(resolve,reject){
    //(4)
    console.log(3)  // 直接打印
    resolve(4)
}).then(function(val){
    //(5)
    console.log(val); // 微任务
})

//(6)
new Promise(function(resolve,reject){
    //(7)
    console.log(5)   // 直接打印
    resolve(6)
}).then(function(val){
    //(8)
    console.log(val);  // 微任务
})

//(9)
console.log(7)  // 直接打印

//(10)
setTimeout(()=>{
    console.log(8) // 宏任务,单比(1)(2)宏任务早
},50)

 上面的代码在node和chrome环境的正确打印顺序是 3 5 7 4 6 8 1 2

下面分析一下执行过程:

  1. すべてのコードは解析後に実行スタックに追加されます
  2. 実行 (1)、マクロ タスク、webapi setTimeout を呼び出します。このメソッドは、コールバック関数をマクロ タスクのタスク キューに入れます。 100ms
  3. (2) を実行します。(1) と同じですが、(1) より少し遅れます。
  4. (3) を実行し、新しい Promise を同期的に実行してから、(4) を実行し、出力します。 3を直接実行し、次にresolve(4)、次に.then()、(5)をマイクロタスクのタスクキューに入れます
  5. (6)を実行します。上記と同じで、最初に5を出力し、次にresolveを実行します。 (6)、次に .then( ) (8) 内の内容がマイクロタスクのタスク キューに追加されます。
  6. 実行 (9)、コードを同期し、直接出力 7
  7. Execute ( 10) (1) および (2) と同じですが、時間が短くなります。50ms 後にコールバック console.log(8) がマクロ タスクのタスク キューに追加されます。
  8. これで実行されます。スタックがクリアされ、マイクロタスク キューのチェックを開始し、(5) を見つけ、実行に追加します。スタックの実行は同期コードです。4
  9. タスク キューを直接出力して実行を終了します。次に、マイクロタスク キューをチェックして、(8) を見つけます。 ). 6
  10. タスク キューを出力し、再度実行を終了します。マイクロタスクを確認します。キューにタスクがありません。その後、マクロ タスク キューを確認します。この時点で 50ms を超えている場合は、console.log が見つかります。 (8) はマクロ タスク キューにあるので、8
  11. を出力し、1 2
  12. を順に出力します。

注: レンダリングもマクロ タスクであるため、レンダリングを実行する必要があります。したがって、実行スタック内の同じスタイルを同時に同期的に変更するコードが複数ある場合は、最後のコードのみをレンダリングします。

関連する推奨事項: JavaScript 学習チュートリアル

以上が古典的な手法: シングルスレッドと非同期 JavaScriptの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はcsdn.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。