26
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

javascriptができるのは非同期処理であって並列処理ではない

Posted at

はじめに

初投稿です
非同期処理と並列処理の違いをやっと理解できたのでそのことについて書きます。

並列処理

まず並列処理は複数スレッドを同時に立ち上げて処理を行う方式のことを言います。(マルチスレッド)

同期処理

同期処理とは通常の実行順番通りにプログラムが実行され実行が終わるまで次の処理に移らない、というような処理方式です。
javascriptは同期処理で処理が行われます。

以下のコードを実行すると、

sample1.js
function sleep(n, name) {
  n = n * 1000
  let start = Date.now();
  while (Date.now() - start < n) { }
  console.log(name)
}

function A() {
  // 重たい処理A
  sleep(1, "A");
}

function B() {
  // 重たい処理B
  sleep(2, "B");
}

function C() {
  // 重たい処理C
  sleep(3, "C");
}

function D() {
  A();
  B();
  C();
}

console.log("start");
D();
console.log("end");

start
// 1秒
A 
// 2秒
B 
// 3秒
C
end

となるはずです。

しかしこの方式では重たい処理があった場合にその処理が終わるまで次の処理に移ることができないというデメリットがあります。

非同期処理

同期処理のデメリットを解消することができるのが非同期処理です。
非同期処理に書き換えたコードが以下のコードです。

これを実行すると、

sample2.js
function A() {
  // 重たい処理A
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('A');
    }, 1000);
  });
}

function B() {
  // 重たい処理B
  return new Promise(resolve => {
    setTimeout(() => {
      console.log('B');
    }, 2000);
  });
}

function C() {
  // 重たい処理C
  return new Promise(resolve => {
    setTimeout(() => {
      console.log("C")
    }, 3000);
  });
}

async function D() {
  await Promise.all([A(), B(), C()]);
}

console.log("start");
D()
console.log("end");
start
end
// 1秒
A
// 1秒
B
// 1秒
C

となります。

function D()の中の処理が非同期で処理されるため、functionA、B、Cの実行が終わる前にendが先に出力されました。
また、function A、B、Cが同時に実行されているように見えます。
これが非同期処理です。

結局何が違うのか

ここまでの説明だけでは、

非同期処理 = 並列処理

のように見えます。自分も少し前まではそう思ってましたが、実際には違います。

以下のコードを実行してみてください。

sample3.js
function sleep(n, name) {
  n = n * 1000
  let start = Date.now();
  while (Date.now() - start < n) { }
  console.log(name);
}

function A() {
  // 重たい処理A
  sleep(1, "A");
}

function B() {
  // 重たい処理B
  sleep(2, "B");
}

function C() {
  // 重たい処理C
  sleep(3, "C");
}

async function D() {
  await Promise.all([A(), B(), C()]);
}

console.log("start");
D();
console.log("end");

javascriptの非同期処理が並列処理だとしたら、このコードは一つ上のコードと同じ結果になるはずです。

しかし実際には、

start
// 1秒
A
// 2秒
B
// 3秒
C
end

となります。
なぜこうなるのかと言うと、javascriptは基本的にシングルスレッドで実行されているからです。
このsleep()という関数はwhileループを使い時間を待っています。
つまり、sleep中はずっとwhileループが回ってます。
そのためpromise.all()しても同期処理と同じ結果になってしまいました。

それに対してsettimeout()という関数はNode core APIのTimerが実行されるためjavascript自体が回っているわけではなく処理を手放し、レスポンスを待っている状態です。
このレスポンス待ち状態に別のタスクを進める、というのがjavascriptにおける非同期処理です。
なので非同期処理は並列処理ではありません。
並列処理っぽく見えるだけです。

まとめ

  • javascriptはシングルスレッドで実行される
  • 非同期処理と並列処理は似ているが別物

非同期処理を理解するのはけっこう難しいですが、これを押さえておくだけでだいぶ理解しやすくなるんじゃないでしょうか?

参考文献

JavaScriptの非同期処理を並列処理と勘違いしていませんか?
同期処理、非同期処理、並列処理のざっくりとした違い
非同期処理ってどういうこと?JavaScriptで一から学ぶ
JavaScriptはシングルスレッドで実行される
内部実装から読み解くNode.js(v11.0.0) Eventloop
async function - MDN web docs

26
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?