はじめに
ミリ秒間隔(もはや同時に叩かれるときもあり)で叩かれるactionのハンドリングをredux sagaでする必要があった。その時の試行錯誤の記録。
ver 1
はじめは普通にtakeで書いていたが、これだとactionを完全に拾えていなく、結構取り漏れている状況だった。
function* request(chan) {
while (true) {
const { payload } = yield take('REQUEST');
// 'REQUEST'のactionが叩かれてはいるが、ここまで来ていないことが割とある
}
}
ver 2
takeEveryを使い、並列に処理するようにしたら取り漏れることがなくなった。
それは良かったのだが、この _request
の処理では各々の _request
で共通して参照/更新したい値があった。しかし、 _request
は並列で動いているため、単純に _request
の処理内にその処理を書いても意味がない。
(仮に_requestがふたつ同時に並列で動いた場合、同時にその値を参照/更新してしまい値がバグる)
イメージとしてはgolangのchanみたいに各並列処理を取りまとめるキューみたいなのが欲しかった。
function* _request(action) {
const { payload } = action;
// some code
}
function* request() {
yield takeEvery('REQUEST', _request);
}
ver 3
最終的にこうなった。
actionChannel
を使えば、そのアクションをFIFOで処理するようなキューを作ってくれる。
ver 2で使っていた takeEvery
だと、アクションのとり漏れは発生しないが並列処理になっちゃうので使わない。
このコードなら下記の // some code の部分は逐次処理されるようになっているので、 共通して使いたい値の参照/更新をそのまま書ける。
function* watchRequest() {
const chan = yield actionChannel('REQUEST');
while (true) {
const { payload } = yield take(chan);
// some code
}
}
参考
https://qiita.com/r-nouchi/items/3809801b817e62b4f60b
https://stackoverflow.com/questions/58973428/how-to-take-multiple-actions-dispatched-with-blocking-call