[JS/REACT]JSはシングルスレッドモデルだということと、非同期処理について
REACTにおいても、多く利用される技術"非同期処理"のPromise、async/await。
なぜ非同期が多く使用されるのか??
ここを復習していきます。やっていきましょう!!!!
JavaScriptはシングルスレッドモデルだということ
JavaScriptは、基本的には非同期で処理を進める言語ですね。
というのも、JavaScriptはシングルスレッドモデルを採用しているため、
1つのスレッドで1つのタスクしか実行できない。
=> JavaScriptが処理しているタスクが重い場合、他のタスクを待たせることになる。
このため、JavaScriptでは、長時間の処理を実行する場合や、外部の処理
(例えば、サーバーからのデータ取得)を待つ必要がある場合は、非同期処理が必要だ。
ということで今回は、以下3点をまとめて解説していきます。
- シングルスレッドとマルチスレッドモデルの仕組みを知る
- 非同期処理の移り変わり
- 非同期処理実装方法について
非同期処理とは
複数のタスクを同時に実行することができる処理方式のこと。
処理を一度バックグラウンドに移すことで、あるタスクを実行している最中でも
その処理を止めることなく、別のタスクを実行できる方式のこと。
簡単に、同期処理と非同期処理の違いについても以下のタブに格納しておきます。
復習項目: 同期処理と非同期処理
同期処理と非同期処理とは
非同期処理は複数のタスクを同時に実行することができる処理方式のことを指す。
一方、同期処理では、処理が完了するまで待機しなければならず、次の処理に進むことができない。
【同期処理】
複数のタスクを実行する際に、上から一つずつ順番にタスクが実行される方式。
- merit:全体を把握しやすい
- demerit:処理完了までに時間がかかり、ユーザーにとってはストレス
【非同期処理】
処理を一度バックグラウンドに移すことで、あるタスクを実行している最中でも
その処理を止めることなく、別のタスクを実行できる方式
=> これを実現するのが、コールバック、Promise , async/awaitで
jsにおいてはajaxと言う技術がある。
※ここに関しては本題で掲載します。
- merit:全体の処理速度を速められる
- demerit:プログラムの全体像が複雑になりやすい
【同期処理と非同期処理の比較】
比較 | 同期処理 | 非同期処理 |
---|---|---|
merit | 全体を把握しやすい | 全体の処理速度を速められる |
demerit | 処理完了までに時間がかかり、ユーザーにとってはストレス | プログラムの全体像が複雑になりやすい |
使用場面 | 処理結果を待つ必要があるもの | 処理結果を待たなくても良いもの、 処理結果がなくても進められるもの |
⚠️非同期処理と並行処理の違い⚠️
- 並行処理ができるかどうか。
=文字どおり複数の処理を同時進行で行うことができるかということ。
非同期処理は処理を止めることなく実行できるというだけの違い!!!
同期処理と非同期処理をもっとわかりやすく例えると…
(下手なイラストだけども描いてみたよ。)
【同期処理タイプのホールスタッフのレストラン】
1番テーブルのオーダー取ったら、キッチンにオーダーを伝えにいく。
料理が出来上がるまでこのスタッフはキッチンの前から動かない。
2番テーブルから呼ばれても、聞こえていないタイプ。笑
1番テーブルの料理を運び終わってから2番テーブルへ行く。
【非同期処理タイプのホールスタッフがいるレストラン】
1番テーブルのオーダーをとって、キッチンに伝えに行く。
戻ってきて2、3番テーブルからもオーダーをとって、またキッチンに伝えに行く。
料理ができたら逐一運ぶ!!
シングルスレッドモデルとマルチスレッドモデルとは
【シングルスレッドモデル】
1つのスレッド(実行コンテキスト)で1つのタスクしか実行できないモデルのこと。
【マルチスレッドモデル】
複数のスレッドで同時に複数のタスクを実行することができる。
1つのスレッドで処理がブロックされた場合でも、別のスレッドで別のタスクを実行することができるもの。
[比較表]
シングルスレッドモデル | マルチスレッドモデル | |
---|---|---|
定義 | 1つの方程式に全ての変数を含む | 複数の方程式に分割され、それぞれに対応する変数を含む |
メリット | 簡単に使用できる。 パラメーター数が少ないため、計算が速く、モデル選択が容易である。 |
変数ごとに最適なモデルを選択できる。 一部の変数についてのみデータがある場合、欠損データの扱いが容易である。 |
デメリット | 複数の変数が相互に影響しあっている場合に、推定値が不正確になることがある。 欠落値に対する対応が困難である。 |
全体的にパラメーター数が多く、計算が遅くなり、過剰適合のリスクがある。 変数ごとに異なるモデルを選択するため、複雑なモデル選択が必要である。 |
採用言語 | JavaScript、Ruby、Python、Lua、PHP | Java、C++、C#、Python(一部)、Go、Rust、Scala |
※言語ごとにモデルが固定されているわけではなく、
JSのようにシングルスレッドでも仕組みを使用しマルチスレッドにもできます。
- スレッド:プログラムの実行単位。仕事の現場だと思ったらいい!
上記の内容を理解しやすい表現にすると、
- javascriptはシングルスレッド = 現場は一つだけ。
- 一つだから管理することも簡単で早い!
- でも、いろんな仕事を一期ですることは大変...。
=> 解決するには??ということで非同期処理が発生した。
= シングルスレッドのはやさ、簡単さを持ちつつ、処理を遅らせない仕組み!!!
非同期処理を実現するために、
JavaScriptにはコールバック関数やPromise、async/awaitなどの仕組みが用意されている。
Promise、async、awaitは非同期処理を扱うためのJavaScriptの構文であり、
AjaxはWebアプリケーションで非同期通信を実現するための技術です。
これらの仕組みを使用することで、JavaScriptで長時間かかる処理を非同期的に実行し、
他の処理をブロックすることなくプログラムを進めることができるようになる。
"JavaScriptは基本的に非同期で処理を進める言語であり、非同期処理を扱うことが重要"
が理解できただろうか??
補足:Web Workerと非同期
Web Worker
-
Web WorkerはJavaScriptにおける並列処理を実現するための仕組み。
Web Workerは、Web APIの一部であり、ブラウザ上でのみ利用可能。
Promise、async、awaitとajaxなどの非同期処理とは異なる。
Web Workerと非同期の違い
これらの違いを理解するために、
上記で説明を書いた、" スレッド=仕事現場 "ということを覚えておいてください。
javascriptはシングルスレッドだから仕事現場は1つ。
また、Web WorkerもJavaScriptにおける"仕組み"なので、シングルスレッドです。
Web Workerと非同期処理も
どう違うか説明できますか??
【非同期処理】
これは、一つの仕事現場の上で、処理を遅らせない仕組みを作成し、マルチスレッドを可能にしようということ。シングルスレッドによる制限を回避するためのもの。
【Web Worker】
一つのメイン仕事現場の裏に、もう一つの仕事現場スペースを拡張作成して、
もう一つの仕事現場スペースは表に出ることなく裏でのみ動く。
メイン仕事現場(メインスレッド)で処理をブロックすることなく、
もう一つの仕事現場拡張スペース(バックグラウンドスレッド)で処理を実行することを可能に。
※バックグラウンドスレッド:
- プログラム内で実行されるスレッドのうち、主にバックグラウンドで動作するスレッドのこと。
- バックグラウンドスレッドは複数作成することができ、効率的な処理を行うために活用される。
非同期処理の遷移と実装
-
JavaScriptでは、非同期処理を実現するために、コールバック関数、Promise、async/awaitなどの機能が提供されている。
-
非同期処理を扱う際には、コールバック関数による実装が一般的だった。
=> 複雑な処理を行う場合には、コールバック関数だけではコードが複雑になってしまう。
ネストが深くなりすぎるが故に、読みづらく"コールバック地獄" とも呼ばれるほどに…。
その問題を改善するためにPromiseやasync/awaitなどの機能が生まれ、
使われることが増えていった。
Promise
-
"非同期処理が成功したか失敗したかの結果を扱うためのオブジェクト"
= 非同期処理が完了した時に実行するコールバック関数を定義することができ、
成功時にはresolve()を、失敗時にはreject()を呼び出すことで、
処理が成功したか失敗したかを判別することができます。
以下に、Promiseの基本的な書き方を示します。
// Promiseの生成
const promise = new Promise((resolve, reject) => {
// 非同期処理
// 成功した場合はresolve()を呼び出し、引数に値を渡す
// 失敗した場合はreject()を呼び出し、引数にエラーを渡す
});
// Promiseの利用
promise.then((value) => {
// 成功時の処理
}).catch((error) => {
// 失敗時の処理
});
ex.) 1秒後にランダムな数値を返す非同期処理をPromiseで実装してみます。
// Promiseを生成する関数
function asyncFunction() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const randomNum = Math.floor(Math.random() * 10) + 1; // ランダムな数値を生成
if (randomNum <= 5) { // 50%の確率で成功
resolve(randomNum); // 成功時はresolve()を呼び出す
} else {
reject(new Error('Error occurred!')); // 失敗時はreject()を呼び出す
}
}, 1000);
});
}
// Promiseを利用する処理
asyncFunction()
.then((result) => {
console.log(`Success! Result: ${result}`);
})
.catch((error) => {
console.error(`Error: ${error.message}`);
});
- asyncFunction()がPromiseを返しており、
then()メソッドで成功時の処理、catch()メソッドで失敗時の処理を定義している。 - setTimeout()関数を使って1秒後にランダムな数値を生成し、50%の確率で成功・失敗を判別しています。
Promiseは、非同期処理を扱うための基本的な機能であり、現在のJavaScriptでは広く使われているが、
複雑な非同期処理を扱う場合は、async/awaitなどの高度な機能も併用することが多い。
thenで繋げていくのは、少々みづらい… ということで、もっと簡単に書きたい!!!!
async/await
-
Promiseをより簡単に扱うための機能であり、非同期処理を同期処理のように書くことができる。非同期処理を扱うコードを簡潔に書くことができるのがasync/awaitなのだ。
■ async: 非同期処理が含まれる関数の宣言に使用され、
■ await: 非同期処理が完了するまで関数の実行を一時停止。
=> asyncは単に関数を非同期関数に変換し、awaitはPromiseの解決を待機するための構文!!!
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
// データを使用して何かを実行する
} catch (error) {
// エラーを処理する
}
}
- 上記はfetch関数を使用してAPIからデータを取得し、awaitを使用して非同期的にデータの取得を待ちます。取得したデータはJSON形式で返されるため、response.json()を使用してJSON形式のデータをJavaScriptオブジェクトに変換。そして、データを使用して何かを実行する処理を記述。
try-catch構文を使用してエラー処理を行うこともできる。
Ajax
- Webアプリケーションでは、Ajaxを用いた非同期通信が一般的に使われる。
Ajaxを用いることで、ページの再読み込みを行わずにサーバーと通信することができる。
これはここで紹介しているので、ここでご覧ください。
今日は以上!!!!!
Discussion
基本的な部分がしっかりと書かれていて、おさらいとしてもすごく良かったです。
ちなみにスレットと書かれている部分が度々あるのですが、これは正しい表記ですか?
コメント、ありがとうございます!!!
確かに"スレッド"と書いてるところもあれば、"スレット"にしてしまっているところがありました!!!
修正いたします。ご指摘ありがとうございます!嬉しいです!!!
さらにいい記事、コードが書けるよう勉強し精進します😌
いい勉強になりました。
スレッドについて他の記事も読みたいです。第二もお願いします!