【JavaScript】非同期処理⑥　マクロタスクとマイクロタスク


#はじめに

Udemyの[【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)の講座の振り返りです。

前回の記事

https://qiita.com/nao0725/items/503217c204dc1f658007


#目的

* 非同期処理についての理解を深める

#本題
###1.マクロタスクとマイクロタスク

**マクロタスク**

* マクロタスクはタスクキューと呼ばれていたもの
* イベントループで処理が回ってきたら、1つずつ格納されているタスクを実行する
* ex. setTimeout

**マイクロタスク**

* マイクロタスクはタスクキューとは別に存在する非同期処理待ちの行列（ジョブキュー）
* マイクロタスクとマクロタスクが同じイベントループで処理が行われた場合**マイクロタスクが優先**される
* イベントループで処理が回ってきたら、全ての格納されているジョブを実行する
* ex. Promise


####例1

コードで確認

```js
// マクロタスク
// タスクの待機時間はなし
setTimeout(function task1() {
  console.log('task1');
});

// マイクロタスク
new Promise(function promise(resolve) {
  console.log('promise');
  resolve();
}).then(function job1() {
  console.log('job1');
});

// グローバルコンテキスト
console.log('global end');

// 実行結果は以下の通り
// Promise
// global end
// job1
// task1
`````

ひとつひとつ確認すると

```js
// 非同期処理でタスクキューで処理される
// グローバルコンテキストが呼ばれた後に実行される
setTimeout(function task1() {
  console.log('task1');
});

new Promise(function promise(resolve) {
  // ここは同期的に処理される
  //　だから一番最初に表示される
  console.log('promise');
  resolve();
}).then(function job1() {
  // ここも非同期処理なのでグローバルコンテキスト実行後
  // マイクロタスクが先に処理されるのでこちらが先に出力される
  console.log('job1');
});

// 先の同期処理"promise"後表示
console.log('global end');
`````

####例2

マクロタスクとマイクロタスクが絡み合った際にどのような処理になるのか

１. マイクロタスクの中にマクロタスクがある状態

`````js
new Promise(function promise(resolve) {
  // まず一番最初は同期的に実行される`promise`
  console.log('promise');
  // マクロタスクだがthenメソッドはresolveの実行を待つので3番目に下記が実行される
  setTimeout(function task1() {
    console.log('task1');
    resolve();
  });
// resolve実行された時に4番目に下記が出力される
}).then(function job1() {
  console.log('job1');
})

// 次の同期処理である下記が2番目に出力
console.log('global end')

// 実行順序は以下の通りとなる
// promise
// global end
// task1
// job1
`````

２. job2,　job3を追加し、job1の後にsetTimeoutを追加する

```js
new Promise(function promise(resolve) {
  console.log('promise');

  setTimeout(function task1() {
    console.log('task1');
    resolve();
  });

  // resolve実行後先にjob1を実行
  // マイクロタスクは格納されている全てのジョブを実行する
  // よってjob2,job3も実行する
}).then(function job1() {
  console.log('job1');
  setTimeout(function task2() {
    // マイクロタスク実行後にマクロタスクtask2を実行する
    console.log('task2');
  });
}).then(function job2() {
  console.log('job2');
}).then(function job3() {
  console.log('job3');
})

console.log('global end');

// 実行順序は以下の通りとなる
// promise
// global end
// task1
// job1
// job2
// job3
// task2
``````

３. 関数queueMicrotaskを呼んでみた場合（job4の実行）

````js
new Promise(function promise(resolve) {
  console.log('promise');

  setTimeout(function task1() {
    console.log('task1');
    resolve();
  });

}).then(function job1() {
  console.log('job1');
  setTimeout(function task2() {
    console.log('task2');
  });
  // thenメソッドが呼ばれた際にjob4が発見されるので、job2,3よりも先に実行される
  queueMicrotask(function job4(){
    console.log("job4");
  });

}).then(function job2() {
  console.log('job2');
}).then(function job3() {
  console.log('job3');
})

console.log('global end');

// 実行順序は以下の通りとなる
// promise
// global end
// task1
// job1
// job4 　←　ここで実行されてる
// job2
// job3
// task2
`````

４. setTimeoutの中にqueueMicrotaskを入れてみる

```js
new Promise(function promise(resolve) {
  console.log('promise');

  setTimeout(function task1() {
    console.log('task1');
    resolve();
  });

}).then(function job1() {
  console.log('job1');
  setTimeout(function task2() {
    console.log('task2');
    // 先にresolveを実行し、job1~3まで実行
    // setTimeoutの関数に従うのでtask2実行後にjob4を実行する
    queueMicrotask(function job4(){
      console.log("job4");
    });
  });

}).then(function job2() {
  console.log('job2');
}).then(function job3() {
  console.log('job3');
})

console.log('global end');

// 実行順序は以下の通りとなる
// promise
// global end
// task1
// job1
// job2
// job3
// task2
// job4 　←　ここで実行されてる
``````

５. queueMicrotaskをPromiseで書き換える

````js
new Promise(function promise(resolve) {
  console.log('promise');

  setTimeout(function task1() {
    console.log('task1');
    resolve();
  });

}).then(function job1() {
  console.log('job1');
  setTimeout(function task2() {
    console.log('task2');
    // queueMicrotaskの書き換え
    const p = Promise.resolve();
    p.then(function job4() {
      console.log("job4")
    })
  });

}).then(function job2() {
  console.log('job2');
}).then(function job3() {
  console.log('job3');
})

console.log('global end');

// 実行順序は以下の通りとなる
// promise
// global end
// task1
// job1
// job2
// job3
// task2
// job4 　←　ここで実行されてる
`````


今日はここまで！

#参考にさせて頂いた記事

* [【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)
* [Let'sプログラミング JavaScript入門](https://www.javadrive.jp/javascript/)