DEV Community

kaede
kaede

Posted on

Web エンジニアリング基礎 -- Part01 同期 or 非同期の処理のコールスタックとタスクキュー

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

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


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

funcA() {
  funcB() 
}
funcB() {
}
Enter fullscreen mode Exit fullscreen mode

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

Image description

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

Image description

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

Image description

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

Image description

最後に funcA が出される。

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

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

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


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

return userTimeLine(groupId)
Enter fullscreen mode Exit fullscreen mode

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

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


非同期処理

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 さんの記事を引き続き参考にする

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

a();
b();
c();
Enter fullscreen mode Exit fullscreen mode

このように

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

とコードを書いて

https://www.jsv9000.app/

このサイトで実行する。

Image description

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

Image description

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

Image description

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

Image description

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

Image description

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

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

非同期まとめ

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


まとめ

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

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

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

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

funcA() {
  funcB() 
}
funcB() {
}
Enter fullscreen mode Exit fullscreen mode

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

この流れになる。

3. 非同期関数の場合

function b() {
  setTimeout(bc,0)
}
function bc() {
  console.log("bc")
}
function c() {
  console.log("c")
}
Enter fullscreen mode Exit fullscreen mode

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

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

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

Top comments (2)

Collapse
 
szabgab profile image
Gabor Szabo • Edited

Hi,

if I may suggest, I think it would be nice if you added a tag to all your posts indicating the language. Japanese, if I am not mistaken. It would make it easier for people to discover them via that tag.

Collapse
 
kaede_io profile image
kaede

Thank you Gabor, I will try from now