Contents
Node.jsはシングルスレッドでどのようにして並行処理をしているか?
並行処理のイメージ例としては「コンビニのレジでお弁当をレンジ温める」間に「お会計をする」です
Node.jsは並行処理もマルチスレッドではなく、シングルスレッドで実現しています
マルチスレッド

- プロセス … 実行中のアプリケーション(Node.jsなら「node.exe」など)
- スレッド … プロセスの中で実際に処理を行う単位。
1つのプロセスの中に複数のスレッドを持つことができます。
Node.jsはイベントループ

イベントループと非同期処理で実現しています
同期処理、非同期処理のちがい
「同期処理」はコードを上から順番に実行し、一つの処理が終わるまで次に進まない方式。
処理が完了するまで他の作業は待機状態(ブラウザでは何もできないといった感じ)になる。
「非同期処理」はバックグラウンドで実行し、メインの処理を止めずに続行する方式。処理完了時にコールバック関数で結果を受け取る。
※非同期処理の結果を受け取って実行されるコールバック関数の処理は同期処理です
イベントループとは
一言でいうと「コールスタックが空になったらタスクキューから関数を移動させる監視プログラム」です
タスクキュー
実行待ちのコールバック関数を順番に保存するデータ構造をタスクキューといいます
setTimeout(() => console.log('完了'), 1000);
上記であれば1秒後にタスクキューに追加されます
タスクキューで待機し、コールスタック(メイン処理の場所)が空になったら順番に実行
(すべての処理は最終的にコールスタックで実行されます)
つまり
非同期処理がバックグラントで走る
↓
非同期処理が完了
↓
コールバック関数の処理がタスクキューに追加
↓
同期処理がすべて終了し、コールスタックが空になる
↓
コールバック関数の処理が実行される

JavaScriptの非同期処理は段階的に改善されてきました
コールバック関数
非同期処理の実装方法でコールバック関数の利用は最も基本的なパターンです
誤解しないように整理すると、コールバック関数=非同期処理ではないです。
コールバック関数は非同期処理の関数の引数です。
非同期処理を行う関数を非同期APIといいます、ブラウザやNode.jsが提供しています
setTimeout(test, 3000); | 非同期処理(非同期API) |
function test() { console.log(“test”); } | コールバック関数(setTimeoutの引数) |
非同期APIでよく使われるもの
- setTimeout
- DOMイベント(
addEventListener
) XMLHttpRequest
(古典的なAjax通信)fs.readFile
(Node.js のファイル読み込み)
これらは 仕様として「非同期で動作する」 と決められています。
setTimeoutのシンプルな例
setTimeoutは第一引数に実行したい関数、第二引数に実行するタイミング
▼書き方
setTimeout(test, 3000);
function test() {
console.log("test");
}
// 無名関数
setTimeout(function () {
console.log("test");
}, 3000);
// アロー関数
setTimeout(() => {
console.log("test");
}, 3000);

console.log("処理1: スタート");
// 非同期処理(2秒後に実行)
setTimeout(function () {
console.log("処理2: これは2秒後に実行されました(コールバック)");
}, 2000);
console.log("処理3: すぐ実行");
▼コンソール
処理1: スタート
処理3: すぐ実行
処理2: これは2秒後に実行されました(コールバック)
処理1 | 同期処理 | ① 同期処理で即実行 |
setTimeout | 非同期処理 | ② 非同期処理で「2秒待つ」がバックグラウンドで走る ※終了後にコールバック(処理2)が実行待ちに入る |
処理3 | 同期処理 | ③ ②の終了を待たずに即実行 |
処理2 | 同期処理 | ④ ②が終了して、タスクキューに「処理2」が追加されかつ、 同期処理がすべて終了しているので、コールスタックに移動して実行される |
コールバック地獄
setTimeout(() => {
console.log("1秒後");
setTimeout(() => {
console.log("2秒後");
setTimeout(() => {
console.log("3秒後");
setTimeout(() => {
console.log("4秒後");
}, 1000);
}, 1000);
}, 1000);
}, 1000);

Promise
Promiseとは、JavaScriptで非同期処理(時間のかかる処理)の結果を扱うオブジェクトです。
// Promiseオブジェクトを定義
function getData() {
return new Promise(function executor(resolve, reject) { // 実行例(わかりやすく名前付き関数で記述)
setTimeout(function() {
if (Math.random() > 0.5) {
// 成功した値を「登録」する
// この値は then メソッドに渡されたコールバック関数の引数 result に渡る(resultでなくて好きな名前でOK)
resolve("成功した値として「データ取得成功」を登録、これは then メソッドのコールバック関数に result として渡ります");
} else {
// 失敗理由を「登録」する
// この値は catch メソッドに渡されたコールバック関数の引数 error に渡る(errorでなくて好きな名前でOK)
reject("失敗: エラーが発生しました(この文字列が catch のコールバック関数に error として渡ります)");
}
}, 1000);
});
}
// 実行例(わかりやすく名前付き関数で記述)
getData()
.then(function onFulfilled(result) {
// resolve で登録した成功の値が result に入る
console.log("成功:", result);
})
.catch(function onRejected(error) {
// reject で登録した失敗理由が error に入る
console.error("失敗:", error);
})
.finally(function onFinally() {
// 成功でも失敗でも最後に必ず実行される
console.log("完了");
});
非同期処理なのは「then/catch/finally に登録された関数」
コード部分 | 処理の種類 |
---|---|
getData() | 同期 |
getData 内の setTimeout | 非同期 |
thenメソッド | 同期 |
onFulfilled、onRejected、onFinally | 非同期 |
console.log(result)(非同期関数内) | 同期 |
resolve
と reject
とは?
resolve(“成功: データを取得しました”) の “成功: データを取得しました” は引数です
この引数は「成功時の値」として Promise に渡されます。
その値が then(onFulfilled) の onFulfilled の引数に入ります。
resolve(value)
→ 成功したときに呼ぶ関数。
→ Promiseの状態を「fulfilled(成功)」にして、value
を.then(...)
に渡す。reject(error)
→ 失敗したときに呼ぶ関数。
→ Promiseの状態を「rejected(失敗)」にして、error
を.catch(...)
に渡す。

.then()
と.catch()
でチェーンできる- コールバック地獄を解決
async/await
- 同期的なコードのように書ける
先ほどの非同期処理をasync/awaitを使用して書くと
// Promiseオブジェクトを定義(この部分は変更なし)
function getData() {
return new Promise(function executor(resolve, reject) { // 実行例(わかりやすく名前付き関数で記述)
setTimeout(function() {
if (Math.random() > 0.5) {
// 成功した値を「登録」する
// この値は await で受け取られる
resolve("成功した値として「データ取得成功」を登録、これは await で受け取られます");
} else {
// 失敗理由を「登録」する
// この値は try-catch の catch ブロックで error として受け取られる
reject("失敗: エラーが発生しました(この文字列が catch ブロックで error として受け取られます)");
}
}, 1000);
});
}
// async/await版の実行例
async function executeGetData() {
try {
// await で Promise の結果を待つ
// resolve された値が result に入る
const result = await getData();
console.log("成功:", result);
} catch (error) {
// reject された値が error に入る
console.error("失敗:", error);
} finally {
// 成功でも失敗でも最後に必ず実行される
console.log("完了");
}
}
// 関数を実行
executeGetData();
awaitはasync関数の中でのみ使用できます async
なしでawait
を使うとシンタックスエラーになります