はじめに
エンジニアとして働き始めてまず理解が難しいと感じたのが、JavaScriptの非同期処理です。
Promiseやasync/awaitなど、なんとなく先輩が書いたコードを真似して非同期処理をなんとなく使用してきたのですが、この機会に理解を深めたいと思いまとめてみます。
このJavaScriptの非同期処理はプログラミングスクールでは、概念をさらっと習うぐらいでした。
しかしいざエンジニアとして働き初めたら、非同期処理に触れない日はないぐらい必要な知識でした。
プログラミング初学者の方に読んでいただけら嬉しいです。
今回は第一弾として、同期処理と非同期処理の概念の違いについて詳しく解説していきます!
最後まで読んでいただければなぜ非同期処理が必要なのか、が分かります。
同期処理と非同期処理の違い
まずJavaScriptにおける「同期処理」と「非同期処理」の違いについて解説していきます。
ざっくりいうとこれはコードの実行順序やタイミングの違いのことです!
同期処理とは?
同期処理は、一つのタスクが完了するまで次のタスクが開始されない処理方式です。
コードは上から順番に実行され、現在のタスクが終了するまで次のタスクは待機します。
console.log("Start") // 1."Start"が出力される const result = task() // 2.時間のかかる処理(task())が実行される console.log(result) // 3.task()の実行が完了したらresultが出力される console.log("End") // 4."End"が出力される
コードを書いた順に、上から処理が走るので、シンプルで問題はなさそうですよね!
しかし、この同期処理には問題点があります。
同期処理の問題点
- ユーザーインターフェイスのブロック: 長時間実行されるタスクがあると、ユーザーはその間アプリケーションを操作できません。例えば、ウェブページが応答しなくなります。
- 効率の低下: 一つのタスクが終わるまで他のタスクが待機するため、全体の処理が遅くなります。
一つのタスクを実行する間(例えばユーザーがアカウントを登録する処理)を行なっている間、アプリケーションが「ユーザー登録処理しかやらん!他は知らん!」状態になります。
そうなるとアプリケーションとしてはとても不便というか使いものにならなくなりますよね。
非同期処理の概念がない世界ではそもそも私たちが普段使っているような、便利なソフトウェアサービスを作ることが難しいのです。
非同期処理とは?
同期処理の問題点を解決するために使うのが「非同期処理」です。
非同期処理は、時間のかかるタスクを待たずに他のタスクを並行して実行する処理方式です。
非同期タスクが完了した時点で、その結果を処理するためにコールバック関数などを使用します。
console.log("Start") // 1. "Start"が出力される
setTimeout(() => {
console.log("コールバック関数実行!")
}, 1000) // 3. 1秒後に、"コールバック関数実行!"が出力される
console.log("End") // 2. "Endが出力される"
このsetTimeout()は指定した時間処理を待ってから実行する、タイマーのような役割を果たす関数です。
setTimeoutの引数に、
() => { console.log("コールバック関数実行!") }
が指定されています。
このように親の関数に引数として渡され、親の関数の中で特定のタイミングで呼び出される関数のことをコールバック関数と呼びます。
非同期処理ではこのように、上から順番に処理を実行していくのではなく、途中で少しタイミングをずらして実行します。
非同期処理の利点
- ユーザーインターフェイスの応答性: 非同期処理を使うことで、時間のかかるタスクをバックグラウンドで処理し、ユーザーはアプリケーションを中断なく操作できます。
- 効率の向上: 複数のタスクを並行して処理できるため、全体の処理速度が向上します。
同期処理と非同期処理の違いの具体例
次に、同期処理と非同期処理の違いをわかりやすく説明するために、実生活の例を使ってみましょう。
同期処理の例:一つのタスクが終わるまで次のタスクを待つ
- コーヒーを淹れるために、まずお湯を沸かします。
- お湯が沸くのを待っている間、他の作業はできません。
- お湯が沸いたらコーヒーを淹れます。
- コーヒーを淹れ終わった後で、次の作業を開始します。
この例では、お湯が沸くのを待っている間、他の作業は全く進みません。
すべての作業が順番に実行されます。
非同期処理の例:タスクを並行して実行
- コーヒーを淹れるためにお湯を沸かし始めます。
- お湯が沸くのを待っている間に、朝食を準備します。
- お湯が沸いたら、コーヒーを淹れます。
- 並行して朝食も準備が進みます。
この例では、お湯が沸くのを待っている間に朝食の準備も進められるため、全体の作業効率が上がります。
実際のコード例
実際のコード例を用いてみていきましょう!
同期処理
function taskA() {
console.log("Task A: Start")
//↓とにかく重い処理
for (let i = 0; i < 1000000000; i++) {
console.log("Task A: End")
}
function taskB() {
console.log("Task B: Start")
console.log("Task B: End")
}
// taskA > taskBの順番で実行
taskA()
taskB()
// 出力順序
Task A: Start
Task A: End
Task B: Start
Task B: End
非同期処理
function taskA() {
console.log("Task A: Start")
// 1秒後に実行
setTimeout(() => {
console.log("Task A: End");
}, 1000)
}
function taskB() {
console.log("Task B: Start")
console.log("Task B: End")
}
// taskA > taskBの順番で実行
taskA()
taskB()
// 出力 Task A: Start Task B: Start Task B: End Task A: End
このように、非同期処理ではtaskA
が完了する前にtaskB
が実行され、全体の処理が効率的に進行します。
非同期処理を活用することで、ウェブアプリケーションのパフォーマンスとユーザーエクスペリエンスを大幅に向上させることができます。
まとめ
今回は同期処理と非同期処理の概念について詳しく触れてきました。
なぜ非同期処理というものが存在し、アプリケーションを作る上で必要なのかお分かりいただけましたでしょうか?
同期処理と非同期処理の違いについて知っているだけでも、役に立つ機会は多いです。
引き続き、次の記事でも非同期処理について詳しく触れていきます!
具体的な非同期処理の実装について学ぶ前に、しっかりと概念について理解しておきましょう。