🤔

eternal-timerの技術点

に公開

こんにちは。
eternal-timerというNode.jsのパッケージを人生で初めて作成しました。
この記事では個人的に努力したことと発見したことについてまとめたいと思います。

作成動機

Discordの身内鯖を持ってるんですけど、Gemmaと喋れるようにしてるんですよ。
タイマーを載せたいと思って、BOTの処理に実装してたらすっごい長くなっちゃってどこかにまとめられないかと思って思いついたのがパッケージ化でした。
あと、プログラミングコンテストが知り合いの鯖であったのでそれにも合わせました。

努力点

人生で初めて非同期処理をまともに使えた
これが結構でかいと思います。
今まで非同期処理を使ってきた場面はfetchのみだったのですが、今回ファイルを触る機会があったのでせっかくだから非同期処理で効率的に処理をしたいと思って実装までできました。
具体的にはreadlineを使ってメモリを効率的に使えるようにしたことと、fsもただreadFileSync()するのではなく、promises.readFile()と書くようにしました。
(ここにきてやっと非同期処理が何なのか、どこが効率的なのかが分かりました。)

あとは後述するクラスをTypeScriptで初めて使えたことも大きいと思います。
今はReactも関数で書くのでクラスを使わないのですが、パッケージとなるとクラスを多用するので、構文をいろいろ学ぶことができました。

もう一つ大きいことが、設計をまともにするようになったことです。
今まで突貫工事のように機能を詰め込めば後でどうにかする感じでやってたのですが、このプロジェクトではどのようにすれば利用者が使いやすいかを考えてそれに合わせて設計することができてよかったです。
エージェントと相談しながら、(コード丸投げというわけではなく)自分が使いやすいと思えるコードを書けたと思います。

エージェントといえば、README.mdやリリースノートは原案をエージェントに書かせてそれをレビューしている感じで管理しています。
正直、人が書くREADME.mdやリリースノートには限界があると思っていて、書き忘れなどが出てくると怖いのでこうしてます。

少しだけ技術的なこと

このコードでは時間を計測するのにtimestampを使用しています。
基本的にsetTimeoutはずれが発生してしまうんですけど、ここではtimestampを使うことでずれがinterval以上に起きないようにしています。

他にもタイマーのパッケージはあると思うんですけど、大規模なもの(DBを使うもの)だとコストがかかるしということでファイルベースのものを書きました。
利用者側はJSONを使う方式(少しだけ重い)とシンプルなテキストベースの方式の二種類から選ぶことができます。

このコードは基本的にreadlineやfs.promisesを使って書いてるので汎用性が高めだと思います。

今回は抽象クラスを使ってみました。
途中までは一つだったのですが、メソッドの中身だけ変わってくるという事が発生してしまったので、急遽変更しました。

テストコードなんですが、ここはJavaScriptで書いてます。
利用者はJavaScriptから使うと思うのでそうしてます。
また、テストはCJSとESMの両方で行われるようにコードを書いてます。

あと、optionalDependenciesでlogtapeを入れることによりログを出すことができます(デフォルトではほぼ出ない)
これは利用者にログの設定を全て委ねる形になっていて、利用者がlogtapeのライブラリで設定することで様々な形のログを提供できます。

まとめるとだいたいこんな感じです。

規模 用途
向いている 小~中規模 BOT用途、単一プロセスでのタイマー管理
向いていない 大規模 分散環境、複数ノードでの共有タイマー管理

初めて知ったこと

筆者はTypeScriptをがっつり触ったのがこのプロジェクトが初めてともいえるので、TypeScriptの仕様には結構戸惑いはしました。

例えばクラスの書き方。
初めて抽象クラスを書いたのですが、継承するときに引数を上書き出来てしまうことに驚きました。
「抽象クラス」なんて言うのだから普通は引数を上書きできないと思っていたので個人的には驚いた点の一つです。

tsconfig.jsonが継承できることにも驚きました。
このパッケージではtsconfig.jsonを継承してESM用のコンパイルオプションとCJS用のコンパイルオプションを指定しているのですが、tsconfig.jsonにどっちで解決するのかを指定しないとIDEに怒られるんですよ。
そこで、tsconfig.jsonではsrc/を除外しておき、ESM用とCJS用のtsconfig.jsonでincludeすることによって設定を上書きすることに成功しました。
この学びは大きかったと思います。(他のプロジェクトでも使えるかと)

筆者はnpmパッケージを今まで一度も作ったことがなかったです。
よってCIのやり方やバージョン管理の方法など、わからないことだらけではあったんですがChatGPTなどと自分の方針があってるかどうかについて相談しながら進めることが多かったです。

おわり

どうでしたかね
少しでも興味を持っていただけた方はぜひパッケージを使ってみてほしいです。
また、IssueやPRも大歓迎ですのでよろしくお願いします
https://github.com/SUKEsann2000/eternal-timer

GitHubで編集を提案

Discussion