Web エンジニアリング基礎 -- Part01 同期 or 非同期の処理のコールスタックとタスクキュー
Tags: webdev, beginners, promise, japanese
Published: 2022-12-31 07:31:50


## why

http://www.02.246.ne.jp/~torutk/seway/dreyfus.html

自分がドレイファスモデルの初心者レベルであることを自覚した。

中級のこれらの項目が以下の理由で満たせていなかったからである。

1: 独力で仕事に当たれるが問題処理に手こずる

* RDB の設計
* ドキュメンテーション

そもそもこれらの項目の経験がほぼないため。

2: ほんの少しだけ決まったルールから離れられる

* リカバリー、障害対応の点において、自分が普段開発していないプロジェクトで全然できなかった。
* 実装や運用の点において、よく作るor使う処理はできるが「他と比べて〜だからこの処理にしている」と説明できたり、よりメンテナンス性の高い処理に自分から改善したりはできなかった。


そのため、RDB の設計の書籍を読んで実際に psql で作ってみるのと
Web の基礎を改めて一通り学習することにする。

基本情報よりも実務に近い記事があるので、それで学習する。

https://zenn.dev/rio_dev/articles/c0da74ae28bdcd

----

# フロント

https://zenn.dev/rio_dev/articles/c0da74ae28bdcd#%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89

## 同期とコールスタック

普通の関数は順次に呼び出されて、コールスタックというところに追加されるらしい。コールスタックが実行できるところだと解釈する。

通常の関数だと

funcA()

funcB()

funcC()

とべた書きされていた場合

https://qiita.com/sho_U/items/f07a4f3e7760a9729f10#%E3%82%B3%E3%83%BC%E3%83%AB%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%82%BF%E3%82%B9%E3%82%AF%E3%82%AD%E3%83%A5%E3%83%BC%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%AB%E3%83%BC%E3%83%97

この記事のように

funcA がコールスタックに入る
funcA が実行されて処理が終わる
fucnA がコールスタックから出される
funcB が積まれる...

と進んでいく

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6yriqv9aacarv5p6qyaa.png)

一つ一つ、実行後の場所に送られて、その後コールスタックに新しい関数が積まれて... と進んでいくと解釈した。

---

## 依存性のある同期関数とコールスタック

```ts
funcA() {
  funcB() 
}
funcB() {
}
```

このような別の関数を呼び出す依存性のある構成になっている場合は
まず funcA が先に積まれる。


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v075muszx8o5xhwmf1ii.png)


しかし funcA が終わる前に　funcB が呼び出される。

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7qwrjzz1c3cxc3guwkkm.png)

なので コールスタックに funcB も積まれる。


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4gmq7f3gbud65fj0nsl8.png)


そして　funcB が先に処理されて出される。


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m2ackd03k5vxhicty7n6.png)



最後に funcA が出される。

このように、コールスタックに積んだ関数に依存性がある場合はスタックに順に積み重ねられていき、後から重ねられた関数から処理が終わって外に出されていく。

デバックモードでよく確認した動きだ。
関数Aの呼び出している関数Bに入って、終わってから呼び出し先の関数Aの関数Bを呼び出した行の次の処理に移って...と。

一見効率が悪く、非同期にしてしまえと思うが、

```ts

const userID = getUser()
const groupID = getUserGroup(userId)

return userTimeLine(groupId)
```

このように、前の処理で取ってきた情報がないと、後ろの処理ができない場合には必要になる。

なので全部非同期にすることはできない。

---

## 非同期処理

https://qiita.com/sho_U/items/f07a4f3e7760a9729f10#%E3%82%B3%E3%83%BC%E3%83%AB%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%82%BF%E3%82%B9%E3%82%AF%E3%82%AD%E3%83%A5%E3%83%BC%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%AB%E3%83%BC%E3%83%97

この sho_U さんの記事を引き続き参考にする

```ts
function a() {
  console.log("a")
}
function b() {
  setTimeout(bc,0)
}
function bc() {
  console.log("bc")
}
function c() {
  console.log("c")
}

a();
b();
c();
```

このように

同期関数の a 
非同期関数の b
b に呼び出される bc
同期関数の c

とコードを書いて

https://www.jsv9000.app/

このサイトで実行する。

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/az2j0xz3evwwty2ua34p.png)

すると、bc は呼ばれたタイミングでタスクキューに入る。
コールスタックには入らない。

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tg1asxxsiy2jmrsy4zwl.png)

そして、キューに入った bc は放置されて


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w9ga5n76nsssfu5sk593.png)


コールスタックに入っている b が処理されてコールスタックから出され


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v8s9l82ka0shk436b4li.png)

まだタスクキューに入っている bc は放置されて
次に c がコールスタックに入り、処理されてコールスタックから出される。


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/akbr7sx13x3cwux8xmvo.png)

最後に、コールスタックに入ろうとする関数がなくなったので
ようやく、bc がタスクキューからコールスタックに入り、

処理されてコールスタックから出される。

### 非同期まとめ

今回の　bc のように、非同期で呼び出された関数は
優先順位の低いものとして「タスクキュー」に入り
優先順位の高い「コールスタック」に入る同期の関数が全てなくなった後に
「コールスタック」に最後に入り、処理される。

---

# まとめ

## 1. 普通の関数、つまり同期関数で依存がない場合

関数　A が呼ばれてコールスタックに入って処理されて出る
次に関数　B が呼ばれてコールスタックに入って処理されて出る

と、順繰りにコールスタックに入れて終わって出してを繰り返す。

## 2. 同期関数で依存がある場合

```ts
funcA() {
  funcB() 
}
funcB() {
}
```

関数 A が依存する 関数 B を呼び出し
コールスタックの A の上に B が積まれる。
B が処理されてコールスタックを出る。
B が処理されたことで A が処理されたことになり
A がコールスタックを出る

この流れになる。

## 3. 非同期関数の場合

```ts
function b() {
  setTimeout(bc,0)
}
function bc() {
  console.log("bc")
}
function c() {
  console.log("c")
}
```

関数 b がコールスタックに詰まれる
bc を呼び出す。
bc がタスクキューに詰まれる
b が終わったものとしてコールスタックから出される。
c がコールスタックに詰まれる
c が処理され、コールスタックから出される
bc がコールスタックに詰まれる
bc が処理され、コールスタックか出される。

このように、非同期の関数が優先順位の低いタスクキューという場所に置かれる。そしてコールスタックに直接入れる同期の後に処理される形になる。

松屋の店員の休憩の時の賄いと同じですね。お客さんの食事が常に先に詰まれるので。