非同期処理の基本

コンピューターの処理速度

  • 計算はCPUで行われる。
  • 命令を実行する単位を1サイクルという。
  • 1サイクルはおおよそ0.3ナノ秒で計算される。Floatの足し算は1.2ナノ秒。(1サイクル1秒とすると4秒)
  • プログラム実行中のデータはPCのメモリ上に置かれている。計算を実行する際はメモリにアクセスして値を取ってくる必要がある。(1サイクル1秒とすると6分)
  • SSDからのデータ読み取りは1000ミリ秒。メモリから取得するよりもSSDからデータの読み取りする方は1000倍ほど時間がかかる。(1サイクル1秒とすると4日)
  • サンフランシスコをインターネット送受信する場合は100ミリ秒。(1サイクル1秒とすると10年)
  • 要するに、ファイルを開くとか、インターネット送受信するのはCPUで計算するよりも、物凄い時間がかかる。まずスケール間を把握することが大事。

同期処理・非同期処理とは

同期処理

  • コードがかかれた順序で実行する。どんなに時間がかかったとしても次の実行を待つ。

非同期処理

  • 時間がかかる処理を実行しつつ、次の処理を実行する。つまり、同時に複数の処理を進めるということ。
  • CPUを待たずに他の処理をすることができるので効率的。

非同期処理で考えなければならないこと

  • 同時並行で処理した場合、どちらが先に終わるのかがわからない。
  • 人間は柔軟に思考できるが、プログラムはどちらが先に終わっても正常に動作するようコードを書かなければならない。

コールバック関数とは

  • ○○が終わったら、××をやってください。の××がコールバック関数。
  • 関数の引数に渡された関数をコールバック関数という。

Promise

3つの要素

  • 処理(時間がかかる処理)
  • 処理が成功した時の値
  • 処理が失敗した時の値

  • Promiseオブジェクトは受け取っても処理内容が確定していない。

  • Promiseオブジェクトには値が確定できたら、呼ばれる処理を登録できる。(thenで)
  • Promiseはコールバック関数のシンタックスシュガー(異なる構文だけど同じ意味のもので、楽にかける方をシンタックスシュガーになる)
  • forやwhileはシンタックスシュガーの関係にある。
const res = fetch("https://api.jikan.moe/v3/search/anime?q=Kimetsu&limit10");
// > ここのresではまだ処理が完了していない(pending)の状態のPromiseオブジェクトが帰ってくる

res.then(res => {
  console.log(res);
})
// >  fetchし終わったら、thenで登録したコールバック関数を呼び出す。
  • thenメソッドは新たにPromiseオブジェクトを返すのでメソッドチェーンを作成できる。
  • thenチェーンの中でエラーが発生したら、以降のthenを全てスキップし、catch節が呼ばれる。
  • また、catchの後にthenを登録すると、エラーが発生しても発生しなくても
  • thenのなかにthenを書く必要はなし。ネストすると、promiseオブジェクトをreturnしなければならず、ミスが発生しやすい。
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
sleep(2000)
.then(() => console.log("2秒たったよ"))
.then(() => console.log("2秒たったよ"))
.then(() => console.log("2秒たったよ"))
.then(() => console.log("2秒たったよ"))
.then(() => console.log("2秒たったよ"))
.catch(() => console.log("失敗"))

f:id:ichikawa-hiroki:20200906175332p:plain

上記の例の場合は、下記のようにコンソールを確認することで、同時にAPIが発行されていることがわかる。

f:id:ichikawa-hiroki:20200906175415p:plain

Promise 応用編

  • resolveに渡した値がthenに渡ってくる。
  • rejectも同様。reject(new Error('エラー'))のように定義する。
function waitOneMinute() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("成功です")
        }, 2000)
    })
}


waitOneMinute()
.then(res => {
    console.log(res);
})
.catch(err => {
    console.log(err);
})