【前編】Node、Deno、Bun?Node代表古川さんと学び直す JS Runtimeの歴史とこれから #フロントエンドの未来

Connpass詳細はこちら

アーカイブはこちら

2024年08月22日、株式会社overflowが主催するイベント「Node、Deno、Bun?Node代表古川さんと学び直す JS Runtimeの歴史とこれから」がオンラインにて開催されました。

佐藤(ahomu)氏:私の自己紹介からします。佐藤歩と申します。ahomuと言うハンドルネームでやっています。トヨタグループの子会社のKINTOテクノロジーズでモビリティサービスに関わっています。よろしくお願いいたします。

続いて 古川さんよろしくお願いいたします。

古川氏:古川陽介と申します。Node.js日本ユーザーグループ代表で活用しています。JavaScriptのカンファレンスのJSConf JPやNode学園の主催をしています。

今回は自分のテーマに沿った話なので登壇させてもらうことになりました。

佐藤(ahomu)氏:では、さっそく古川さんお願いします。

Offers」では、エンジニア・PM・デザイナー向けにキャリア、スキル、働き方についての役立つイベントを開催しています。無料登録・ログインで、人気のイベント動画は今すぐアーカイブ視聴可能です。動画を視聴して、最新の技術トレンドや実践的なノウハウを手に入れましょう!

【限定配信】アーカイブ動画を今すぐ視聴する!

LT:JavaScript Runtime とはなにか

古川氏:私は古川と申します。Xでは@yosuke_furukawa、GitHubではyosuke-furukawaでやっております。

それではJavaScript Runtimeの話です。

Node.jsの公式ホームページには、以下のように書かれています。

Denoの公式ホームページにも似たようなことが書かれています。

同じくBunにも以下の通り書いてあります。

このようにJavaScript Runtimeと言う言葉が出てくるのですが、そもそも「Runtimeとは何?」と皆さん疑問に思ったことがあると思います。

そもそもRuntimeってなんだ

「Node.jsはプログラミング言語ではなのですか?」と聞かれるたびに、「プログラミング言語はJavaScript」だと説明しなければならず、では「Runtimeとは」、「Runtime環境とは何」……という堂々巡りになるので、そこの話も深堀してお話しします。

日本語の辞書的な意味は形容詞で「実行中の~」とあります。

名詞として使っているときもありますが、Runtimeの辞書的な意味では形容詞として使うことが一般的だそうです。

実は基本的には、Runtime System, Runtime Library, Runtime Environment のように使います。Node.jsはRuntime Environmentと書いてありますが、省略してある感じです。

プログラミング言語は構文として解析して処理する compileの時間と、実行する run timeの時間にタイミングが分かれると考えられていて、Runtimeはその時の実行する時間のことを指しています。実行している間に必要な仕組みを指しているものをRuntime System (実行中のシステム)やRuntime Environment(実行中の環境), Runtime Library(実行中のライブラリ)といいます。

プログラミング言語はいわゆる構文と呼ばれている部分とそれらを実行しているタイミングに分かれていて、Runtimeは実行している部分に目を向けた時の話をしています。

JSの分野(Node.js, Deno, Bunの例)ではRuntime とはRuntime Environmentを省略したものとして使っています。

他の言語ではと言いますと、PHPの場合、Zend Engineと呼ばれるEngineがコンパイル及びRuntime Engineとして振る舞っていると書かれていました。

なぜ書かれていましたと言うのかは、私がPHPをあまり詳しくないからです。

このZend Engineと呼ばれているものがコンパイルも、Runtime中のOSの抽象化及びファイルの操作もやっていると書いてありました。

最近Zend Engineもアップデートされていて、PHP8はZend Engine3だと書いてありました。

JavaはJSに近いと言っていますが、JSに近いのではなくJavaが 総本家なんです。

Javaの場合はJava Runtime EnvironmentとJava VMに分かれています。VMがOS, CPUの抽象化を主に担い、Runtime Environmentはそれ以外の処理を任されています。(JSに近い)

JSもV8とCPUの抽象化をしていて計算の部分をそこが担っています。それ以外はNode.jsが担う環境になっています。

Rustの場合、RustのRuntimeが何かという問い合わせがあるようですが、Runtimeの明確な区分けみたいなものはないんです。Runtimeはありますが、RustでRuntimeが無いという記事などがたまにあり、それはGC処理が存在しないことを指しているんだと思います。メモリ管理もRuntimeの一部なんです。

非同期処理async IOはtokioなどの3rdparty libraryが担っていて、Rust本体ではやっていません。

Runtimeが指しているものがそれぞれ違いますよね。GC処理や非同期通信処理などをするとき、Rustは存在しないということです。

つまり言語によってすごく曖昧です。

「Runtimeは何ですか」と言う質問は、じつはすごく曖昧な質問なんです。

もっと言えばここからここまで実装するもの"Runtime Environment" だ。という明確な定義はないと思っています。コンパイルタイムのものとRuntimeのもので分かれているという考え方なんです。

なので、曖昧でぼやけています。必要としているものが違うんです。

なぜかというと、言語設計者によってプログラミング言語とそれを取り巻く周辺の機能を、どう階層分けして表現しているかが結構違います。Rustはプリミティブな言語でシンタックスな部分や構文解析の部分を頑張る結果、コンパイルファイルの手厚い処理はあるけれどRuntimeにはGC処理がないなど、言語の哲学としてそこを分けている感じです。

一方GCをコアの機能として表現しているところもあります。

JavaのVPNはGCをコアの機能として持っているので、言語設計者によりどのようにレイヤリングして何がどの機能かをどう分けるかが全く違います。

そのためRuntimeと言われても、モヤっとしているんです。

JavaScriptは僥倖でした(笑)。

ブラウザが進化する過程でJavaScriptは後から入ってきました。その時に言語のコア仕様(ECMAScript)が作られたので、周辺の機能仕様が分かれてブラウザやサーバに組み込まれている感じです。

言語のコア仕様とサーバ用途で利用する機能群を構成したものが、 Node.jsでありDenoでありBunのようなものが Runtimeと呼ばれているものになりました。

これは私の定義ですが、「BrowserもRuntime」という曖昧な定義の内側に入れてしまえばRuntimeと言ってしまっていいと思います。

なので、Runtimeという曖昧な定義を理解しようとするのではなく、JavaScriptやRustなど具体的な言語環境において、どのような機能が言語側で提供されていて、どのような機能が周辺側で定義されているか理解した上で具体的な機能を理解するほうが分かりやすかったりします。

JavaScriptと周辺を含めたものには、どんな機能があるかの話をさせてもらいます。

JavaScript(+runtime)機能一覧

本来はもっとたくさんあるのでざっくりではありますが、機能一覧を出してみました。

この中で、どこがJavaScriptにおいてコア(JS Engine)として提言された機能で、どこがRuntimeとして定義された機能かを分けました。

すごく厳密なことを言うと、メモリ管理はRuntimeもしているので、どれもオレンジっぽくなってはきますが、理解を簡潔にするとparserやジットのようなものはJSエンジンです。フロントエンジニアに分かりやすいJavaScripのAPIは完全にJS EngineがJavaScriptの機能として提供しているものです。

一方そうでないものがRuntimeの機能になっていて、ファイルのストレージに対してRequestを送る、ネットワークのRequestを送る、FSヘッジ、DomもRuntimeと考えています。

非同期処理だけ両方としているのは、プロミスが入ったタイミングで非同期処理になり、JS側でも非同期処理をしなければいけなくなるからです。Runtime自身のブラウザが持っている「クリックした」やNode.jsが持っている「ファイル操作をした」も全て非同期処理なので、そのようなものをハンドリングしなければならず、非同期処理の機能を両方で持っているイメージになっています。

整理しようと思って書いたけれど、分かりづらかった図がこちらです。

JSエンジンがコアにあって、たまたま非同期処理がはみ出ている感じですが、他はJS Runtimeとして包まれているという概念的に理解していただければと思います。

ここまでのまとめ

ここまでをまとめると、画像のとおりです。

ここまでが「Runtimeとは何か? 」についてです。

今までブラウザもRuntimeだと言い、サーバとブラウザをあまり分けずにRuntimeと紹介してきましたが、これはサーバ周りのRuntimeの動きが最近面白い動きを見せていると思っているからです。

WinterCG

聞いたことがあるかもしれませんが、「Winter CG」と呼ばれている仕様化団体です。WEBの総合互換性を保つための、Runtimeのためのコミュニティグループです。The Web-interoperable Runtimes Community Group を略して「Winter CG」です。

これには2つ目論見があると思います。

ひとつは、Server 用途で利用されている Runtime の最小限の共通APIセットを作り、これらが実装されたもので相互互換性を上げようという試みです。

「Winter CG」の中に書いてあるcommon APIセットを実装してそれしか使わない縛りでコードを書いて相互作用を作っていくのが一つ目の目論見です。

二つ目が、その中で「なるべく」ブラウザに近いAPIを採用しようとしていることです。ブラウザ側で提案されて採択されているAPIにサーバ側が近づけようとしています。

概念図的には、ブラウザのAPIとサーバのAPIがあり、JSのAPIはどちらでも使えるものもありますが、そうではないところに配置するfetchやストレージの操作など共通項になりえるところをなるべく大きくする試みだと思ってください。

ここからが本題です。今までが前置きです。

Node.js / Deno / Bun それぞれの違いについて

いくつも違いがあるので、なんとなくもっと書きたいと思いましたが、いったん戦略面と体制面だけ書いてみました。

戦略面

・WinterCGへの対応を進めつつ、お互いの良いところがあれば後追いで採用していく方針

→先ほどのWinterCGと呼ばれているサーバのAPIと各種Runtime Environmentとの差を埋めるWinterCGへの対応を進めて行きつつ、お互いの良いところがあれば採用していく方針は共通戦略だと思います。

・Node.js では最新で strip-types や storage の対応が進んでいる(Deno, Bunの機能への追従)

→スクリプトがそのまま型を除去して動く仕組みが入ったり、ストレージを入れてWEBとの親和性を高めたりする対応が進んでいます。DenoやBunでは既に提供されている機能なので、それを後追いで追従していく感じです。

・DenoはNode.js Compatibility を上げてnpmの資産も使えるようにしている(Node.js既存資産への追従)

→DenoはNode.js Compatibility(互換性)を上げてnpmに既に上がっているNode.jsのサードパーティのライブラリをなるべくDenoでも動かせるようにしています。これは既存資産への追従です。

・Bunは WinterCG へは参加表明していないが、 Web API Interop はできる範囲で進めている。さらにNode.jsのCompatibilityを上げてnpmの資産も使えるようにしている(Node.js既存資産への追従)

→Bunも基本は一緒ですが、WinterCGには参加表明していません。これは複雑な気がしていますが、Bunのリーダーはオピニオンが強めなのでおもしろいです。

調べれば出るかもしれませんが、「俺は幼稚園の徒競走みたいに手をつないでゴールをしたくない」のようなこと言っているので、見てみると面白いかもしれません。

それは別として、 Web API Interop はできる範囲で進めています。APIに関しては、参加の表明はしていませんが、無視もしていません。Node.jsのCompatibilityを上げて、npmの資産も使えるようにしています。

全体が機能を追従していくことをしているので、あまり差分が無いようになっています。

一方で差別化のポイントももちろんあります。

・機能面に関しては大きな変更はせずに、小さい変更の中で他のRuntimeとの差を減らしていっている。

→機能面は成熟しているので大きな変更はしません。大きな変更をすると既存資産に大きな影響を与えるので、小さな変更の中で他のRuntimeとの差を減らしています。

・非機能要件面でのパフォーマンス向上、セキュリティ対応、運用時の分析方法の追加などが行われている。

→URLのパーサーを早くしたなどです。

既存のAPIや機能面の拡充はありませんが、非機能面ではかなり追加が行われているので、成熟している印象があります。

・Deno Fresh などの フレームワークも公式が同じorganizationで開発しており、周辺のツール類はそこでサポートされている印象。

→deployでサーバ環境へ置く仕組みも同時に開発しているので、周辺ツール類はそこでサポートがされているイメージです。

JSRはいわゆるnpmのようなものだと思ってもらうと分かりやすいと思います。JavaScriptのサードパーティライブラリを補完するためのレジストリになります。

npmと全然違うのはタイプスクリプトをそのままソースとしてパブリッシュすることができます。

その中で勝手にCompileしてJavaScriptへ変換してくれるので、こっちでは何も考えなくてもいいのです。

npmはタイプスクリプトの状態からどうするかを考えなければなりません。配信するときにどうするか、(ESモジュールのまま配信するとそのままだと動かないかもしれないので、common.jsでも配信して……)など考えなくてもいいのです。

npmのパブリッシュ先からJSRを経由して持ってくることもできます。既存資産からnpmをターゲットにしながらJSRを使い続け、どこかでJSRに上がったらJSR経由で取得することもできるので、移行もしやすいようになっています。

ESモジュールを最初のターゲットにするので、CGS、common.jsが基本となっているnpmとは差別化されるポイントだと思っています。ここが流行るかが議論される部分だと思います。

新しいエコシステムを作ろうとしている感じです。

新しいレジストリに備えてNode.jsからユーザーのエコシステムをどこまで持ってこられるかがポイントになると思います。

・blog を見ても毎回必ずパフォーマンス改善とNode.js互換性向上が書いてある

→最初のブログで「Next.jsで動く」と書いてありましたが実際は部分的にしか動きませんでした。でも、動くアピールをすごくしていました。

・かなり独自路線。思いつきみたいなことをサッと実装している

→Bunの中でC言語かけたらどうかな。と思いついたら、翌日には実装できているスピード感です。

私が勝手にBunのキャッチフレーズを考えました。

「Performance is all you need!! And crazy ideas are all I need!!!」

俺が思っているアイデアこそが最高と言う部分があります。だからこそWinter CGなどに入ってないんでしょうね。足並み揃えてゴールなんてしたくないという気持ちがあるのだと思います。

体制面

・リーダー不在

→ネガティブな意味ではなく、あえていないという意味です。

・大規模OSS開発者体制、各分野ごとにWGを作り、TSCと呼ばれる委員会で方向性を決める。コミッターを成長させて体制を継続させるようにしている。

→大規模OSS開発者体制と名付けてみました。企業や組織みたいです。

リーダーが存在せずに各メンバーの合意で決まる所はWebの標準化委員会と似ていると思っています。

だからこそ動きが早いわけではありませんが、いろいろとやれている感じです。

Vercelが一番近いかなと思います。OSS+商用でお金を稼ぎながら開発者の意思も反映しているところが似ていると思っています。

RedisとかVercelのOSS + 商用での戦略に似ています。

体制は作られていて、開発する人が増えてきているのですが、まだこれから感はあります。

ここまでのし上がってきているのは、リーダーのJarredさんが爆発的な開発能力を発揮しているからだと思います。

まとめ

以上で私からの発表は終わります。

佐藤(ahomu)氏:ありがとうございました。

続いて、運営で用意していたディスカッションです。

深堀してお話を伺いたいテーマをいくつか用意しているので、そこら辺を消化しつつ質疑応答やzoomチャットのコメントに答えていこうと思います。

【後編はこちら】


Offersエージェント」では、業界で活躍するプロフェッショナルがあなたの転職を徹底サポート。CxO経験者を含む現役エンジニア・デザイナー・プロダクトマネージャーが在籍し、職種に特化した専門的なアドバイスをご提供・非公開求人の紹介も可能です


この記事をシェア

関連記事


副業・フリーランス

プログラミング

インタビュー

デザイン

お金

採用・組織

イベントレポート

転職