【JavaScript】非同期処理をコールバック関数をしようせずにPromiseで実装できる理由

非同期処理とは

同期処理か非同期処理かは関数(やメソッド)に対して使う言葉です

同期処理は、ある処理が完了するまで次の処理に進まない動作を指します。
一方、非同期処理は、処理の完了を待たずに他の処理を並行して進めることができ、メインスレッドが待機状態になることはありません。

一般的にはこのように説明されますが、より正確には「その関数が非同期APIであるかどうか」が本質的な違いです。

非同期APIとは

非同期APIとは、ブラウザやNode.jsといったJavaScriptランタイムが提供する、非同期的に結果を返す仕組みをもつ関数のことです。

例)たとえば setTimeout や XMLHttpRequest、addEventListener

async/awaitPromise を使わない場合、JavaScriptで非同期処理を実現する手段は、基本的に「非同期API」を利用することだけです。

例)非同期処理

setTimeout(() => {
  console.log("非同期処理完了");
}, 1000);

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

// 出力結果
先に実行
非同期処理完了

なお、setTimeoutの引数を0にして、0ミリ秒後としても出力順番は同じです(先に実行→非同期処理完了)

理由はsetTimeoutで登録された内容はタスクキューにスタックされて、メインスレッドの全ての同期処理が完了後にコールされるからです

例)コールバック関数を使用した非同期処理

const fs = require('fs');

/**
 * readFile
 * @param {string} filename - 読み込むファイルのパス
 * @param {function} callback - 読み込み完了後に実行されるコールバック関数
 *   @param {?Error} callback.err - Node.js が内部で自動的に渡すエラーオブジェクト(発生しない場合は null)
 *   @param {string|Buffer} callback.data - Node.js が内部で自動的に渡す読み込み結果
 * @returns {void}
 * @description
 * Node.js の組み込み非同期API。ファイルの読み込みが完了すると、
 * Node.js ランタイムが内部的に callback(err, data) を呼び出す。
 */
fs.readFile("./input01.txt", 'utf8', (err, data) => {
    if (!err) console.log("1");
});

fs.readFile("./input02.txt", 'utf8', (err, data) => {
    if (!err) console.log("2");
});

fs.readFile("./input03.txt", 'utf8', (err, data) => {
    if (!err) console.log("3");
});

fs.readFile("./input04.txt", 'utf8', (err, data) => {
    if (!err) console.log("4");
});

fs.readFile("./input05.txt", 'utf8', (err, data) => {
    if (!err) console.log("5");
});


// 出力結果は