[V2] Promise.allを順序じゃなく「意味」で束ねた ― keyedPromiseAll
経緯
Promise.allを順序じゃなく「意味」で束ねた ― keyedPromiseAll
以前この記事をかいたのだけど、失敗に終わったので、今回はアプローチを変えて再実装
どうにか、全体の辻褄が合うところまで出来上がりました。
最初のバージョンでは、Promise配列受け取って待つやつはかいていたので、あれだけでいいなら済だったんですが、やっぱり実行までやりたいなぁと。
中身の話
失敗版は、FPっぽくこだわりすぎていたことを反省して、今回はクラスで実行体(KeyedFunc)を管理させる方針に切り替えました。
コレをやることで、型の情報は個々だけに集約して外に染み出さないようにしています。
またPromiseAll部分で引数を受け取る際に、KeyedFunc<T>のTで関数の型を推論させることで、戻り値の型生成に一役買っています。
明確に理解ないといけなかったのは、型の制約部分と引数の関係で、制約自体は受け入れ範囲、変数定義は実体を受け入れる型なので、この違いをミスってしまうと、色々壊れますw
関数の実行後、getResult()で結果を受け取るのですが、コレを保持しておく方は、テーブルの関数から割り出すしか無く(思いつかなかった)、as を使って実体を作り設定していく形をとっています。
そもそもRecord型って、 形指定して {} で突っ込めないから、asを使う羽目にはなるんですが。
余談
真面目にコレを考え続けて、気がついたら2日ほど経過していました。
結構苦心したので、出来上がったときは、大分にやけてました・・・きもちわるいですね。
最近、コード書いているときに笑っていることが多いらしく、家族からも不審な目で見られているので、少々心配ではありますが、これからも頑張ります。
コード
共通で使う型の定義
AsyncFuncは、非同期実行の関数用型定義(Promiseの戻り値を強制)
ResultTypeは、戻り値をそのまま扱わず、一枚ラップする仕組みですね。
catchした場合のthrowされる値は、unknown担ってしまうので、失敗として扱う仕組みを入れています。
KeyedFunc - 実行する関数の保持
この関数が肝になります。コンストラクタで関数タイプを受け取り、分解
情報を保持して、実行に備えます。
exec()の実行時は、外に頼らず中で完結させています。結果自体はInstance内に保持させて、あとでresult取得に利用します。
KeyedPrimiseAll - PromiseAllのKey付与版
ここが今回の肝になる部分。
Key付きのKeyedFuncのRecordを受け取って、色々やります。
書き方的に珍しいと感じているのは
type KeyedFuncs<T extends Record<string, AsyncFunc>> = {
[K in keyof T]: KeyedFunc<T[K]>
}
こういうやつですね。Keyを参照させて、そのKeyのときの型を引っ張り出すやつ。
乱用は本当に辞めたほうが良いとは思いますが、どうしてものときは役に立ちそう。
KeyedFunc<T>で実行する非同期関数の型を取り出しているので、コイツを利用して応答の型を決めています。ほんとはgetResultから取れるのが理想なんでしょうが、ちょっと仕組みが思いつかなかった部分です。
やり方はある模様ですが、どうなのそれ?って感じだったので、一旦不採用
型の扱いの記事をまた書くかも知れないです(クラスの型抜きだし関連)
また、Tの推論が大分気持ち悪いことになってますが、逆推論とやらが走っているらしく合法とか。
受け取った方は、KeyedFuncのレコードでTはレコードじゃないはずなのに、TをRecordとした制約が通ってしまうあたりが気持ち悪いポイントです。
keyed - 実行メソッドをつなぐ仕組み
あとで実行するときにでてきますが、
keyed → with → with → ... → build
が出来るようにしています。(ちょっとかっこいいでしょ!)
実際に使っているのはこの辺ですね
元々、配列に詰め込んだりしてたんですが、関数の引数が配列とか、ちょっとイマイチな部分もあったので、メソッドチェインでつなぐ仕組みを作ってみました。
withを呼ぶたびに、型が拡張されていくので、大分おかしな動きをします。
handleResult
結果の確認がめんどくさくなってしまったので、ヘルパー関数。
成功時と失敗時の動きをかける仕組みにしています。
実行部分
非同期関数を作って、セットして実行という動きをしてます。
今まで作ったものを使って動かしていますが、大まかな流れは
関数定義 → メソッドチェインで非同期関数をセット → 走らせるとまとめて走って
全部終わったら応答返してくれる感じになってます。
Discussion