0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[JavaScript][Synchronous / Asynchronous] 非同期処理はなぜ起きるのか

Posted at

概要

JavaScript は、コードを 上から順に同期的に実行する言語です。
しかし実際の開発では、setTimeoutfs.readFilefetch などを使うことで
**処理が「あとで実行される(非同期)」**ように見えます。
mdn web docs – Asynchronous JavaScript

つまり、
JavaScript 自体が非同期なのではなく、
環境(Browser / Node.js)が提供する API の実装によって
非同期処理が発生している
という点が重要です。

本記事では、

  • どのような処理が非同期になるのか
  • なぜコールバック関数で非同期が成立するのか

実装視点で整理します。

目次

基本構文

同期処理の例

console.log("A");
console.log("B");
console.log("C");

出力結果

A
B
C
  • 上から順に実行される
  • 前の処理が終わるまで次に進まない
  • JavaScript の基本的な実行モデル

非同期処理の例

console.log("A");

setTimeout(() => {
  console.log("B");
}, 0);

console.log("C");

出力結果

A
C
B
  • setTimeout 内の処理は「あとで」実行される
  • JavaScript の実行自体は止まらない

非同期を生む 3 種類の処理

JavaScript における非同期処理は、主に次の 3 種類に分類できます。

I/O(入出力系)

代表的なメソッド

分類 メソッド 実行環境
ファイル fs.readFile Node.js
ファイル fs.writeFile Node.js
ネットワーク fetch Browser / Node
ネットワーク XMLHttpRequest Browser
DB client.query Node.js

なぜ後回しになるのか

  • OS・ネットワーク・DB と通信する必要がある
  • 完了までにかかる時間が事前に分からない

そのため、
処理完了後にコールバックを実行する設計が採用されています。

Timer(時間系)

代表的なメソッド

分類 メソッド 実行環境
タイマー setTimeout Browser / Node
タイマー setInterval Browser / Node
即時実行 setImmediate Node.js
フレーム requestAnimationFrame Browser

なぜ後回しになるのか

  • 「今すぐ実行」では意味がない
  • 指定した時間・条件が成立するまで待つ必要がある

そのため、
処理は一度 予約(キュー登録) されます。

Event(イベント系)

代表的なメソッド

分類 メソッド 実行環境
DOM addEventListener('click', …) Browser
DOM addEventListener('input', …) Browser
Node process.on('exit', …) Node.js
Stream stream.on('data', …) Node.js

なぜ後回しになるのか

  • ユーザー操作や外部イベントは
  • いつ発生するか分からない

そのため、
発生時に呼び出す関数を登録して待つ仕組みになります。

コールバック関数と非同期の関係

結論

コールバック関数だから非同期なのではありません。
コールバック関数が「あとで呼ぶ場所」に登録されることで、
非同期が成立します。

同期コールバック

function run(fn) {
  fn();
}

run(() => {
  console.log("今すぐ実行");
});

console.log("");

出力結果

今すぐ実行
次
  • コールバック関数を使っている
  • その場で呼ばれている
  • → 同期処理

非同期コールバック

setTimeout(() => {
  console.log("あとで実行");
}, 0);

console.log("先に実行");

出力結果

先に実行
あとで実行

実際に起きていること(概念)

1. JavaScript が setTimeout を呼ぶ
2. タイマーを環境に登録
3. コールバックを Task Queue に登録
4. JavaScript の実行が一段落
5. イベントループがコールバックを実行

活用例

1. I/O による非同期処理

const fs = require("fs");

fs.readFile("sample.txt", "utf-8", (err, data) => {
  console.log(data);
});

console.log("読み込み中");

ポイント

  • readFile は非同期 API
  • 読み込み完了後にコールバックが実行される

2. Promise による非同期処理

fetch("https://example.com/data.json")
  .then(res => res.json())
  .then(data => {
    console.log(data);
  });
  • .then() もコールバック
  • 登録先が Microtask Queue になる点が異なる

了解です 👍
では 構成・文体・粒度はそのまま にして、
「活用例」を拡張します。

ポイントは:

  • 既存の例は維持
  • 「import が必要な非同期 API の例」を明示的に追加
  • 「なぜ import が必要なのか」がコードから自然に読み取れる形

3. Event による非同期処理

document.addEventListener("click", () => {
  console.log("クリックされました");
});

ポイント

  • addEventListener は DOM オブジェクトのメソッド
  • document 自体が Browser 環境でグローバルに提供されている
  • クリックイベントが発生した時点でコールバックが実行される
  • イベント発生タイミングは事前に分からない

4. import 構文を使った非同期 I/O の例(ES Modules)

import { readFile } from "fs/promises";

const data = await readFile("sample.txt", "utf-8");
console.log(data);

ポイント

  • fs/promisesNode.js のモジュール
  • import が必須
  • readFile は Promise を返す非同期 API
  • await により「同期風」に記述しているが、実体は非同期

実行キューの対応関係

種類 登録先
Timer Task Queue
I/O Task Queue
Event Task Queue
Promise.then Microtask Queue
async / await Microtask Queue

参考リンク

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?