10月20日メジャーアップデート!「Node.js v15」の新機能とは?
エンジニアになろう!
10月20日にメジャーアップデートされた「Node.js v15」について解説
2020年10月20日に、Node.js のメジャーアップデートがありました。
というわけで、バージョン15 が、遂にリリースされました!
このアップデートにより、前のバージョンから変更があったり、実験的に新しい機能が追加されていたりします。
Node.js は、半年ごとにアップデートされていますが、「最新版がどういったものなのか、よくわからない」という方が多いのではないでしょうか?
この記事では、
- 「Node.js v15」で導入された機能
- 前バージョンからの変更点
について、詳しく解説していきたいと思います!
Node.js v15とは?
「Node.js v15」は、2020年10月20日にリリースされた、Node.js の最新バージョンです。
(※2020年12月時点)
ちなみにこのバージョンは、「15」という数字が入っていることから、奇数バージョンと呼ばれています。
サポート期間は、比較的短いですが、最新の機能が導入されてるのが特徴です。
つまり、最新の機能を、先取りすることができるわけですね!
では次から、いよいよ「v14 からの変更点」や「新しく導入された機能」について、見ていきましょう!
Promiseの処理を中断できる「AbortController」
HTTP リクエストをするときは、Fetch API を使うことが多いかと思います。
Fetch は Promise を返しますが、Promise の処理を途中で止めたい場合は、どうすればいいのでしょうか。
それを実現するために、「AbortController」という仕組みがあります。
AbortController は、Fetch など Promise の処理を、途中で止めることができる機能です。
これは、元々 Web 標準に存在する機能で、前述した通りの機能を持っています。
【MDN Web docs:AbortController】
https://developer.mozilla.org/en-US/docs/Web/API/AbortController
Node.js v15 では、この機能が試験的に組み込まれています。
具体的なコード
例えば、fetch の処理を3秒後に中止したいときは、以下のように記述してみましょう。
1let controller = new AbortController();
2setTimeout(() => controller.abort(), 3000);
3
4fetch('example.com/aaa', {
5 signal: controller.signal
6 })
7 .then(res => {
8 console.log(res);
9 })
10 .catch(err => {
11 if (err.name == 'AbortError') {
12 alert("Aborted!");
13 } else {
14 throw err;
15 }
16 });
このコードでは、まずコントローラーの作成をしています。
1let controller = new AbortController();
次に、AbortController の signal プロパティを、fetch のオプションに設定します。
1fetch('example.com/aaa', {
2 signal: controller.signal
3})
これにより、fetch は signal を、リッスンするようになるのです。
あとは、fetch を中断したいタイミングで controller.abort() を呼べば、その時点で reject されるようになります。
1setTimeout(() => controller.abort(), 3000);
こちらの例では、setTimeout で、3秒後に abort のシグナルを送るように設定しました。
タイムアウトのような処理を実装したいときに、AbortController が使えますね!
unhandled rejectionsが「エラー」を投げる
unhandled rejections とは、
- reject された Promise
- async 関数内で throw されたエラー
が、catch で処理されずに、reject されたままとなっている状態を指します。
Node.js v14 まで、unhandled rejections は、「warning」となっていました。
しかし v15 からは、unhandled rejections は、「エラー」になります!
ちなみに、このような変更を、「破壊的変更」といいます。
具体的に、どういうコードがエラーになるのか、実際に見ていきましょう。
具体的なコード
Node.js v14 以前では、unhandled rejections になると、以下のような warning が表示されます。
1$ node -e "Promise.reject()"
2(node:4560) UnhandledPromiseRejectionWarning: undefined
3(node:4560) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
4(node:4560) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
こちらのコードでは、Promise が reject されています。
ですが、catch でハンドリングされていないので、このような警告が出ています。
では、Node.js v15 だとどうなるのでしょうか?
1node:internal/process/promises:218
2 triggerUncaughtException(err, true /* fromPromise */);
3 ^
4
5[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "undefined".] {
6 code: 'ERR_UNHANDLED_REJECTION'
7}
v14 とは打って変わって、エラーが出ていますね。
実際にバージョンをアップデートするときは、現状のコードでエラーにならないか、注意が必要です。
「QUIC」を試験的にサポート
QUICの概要
Google 開発の QUIC は、UDP 上で動作する、トランスポート層のプロトコルです。
QUIC は、次世代 HTTP である HTTP/3 にも、プロトコルとして採用されています。
QUIC の特徴は、リアルタイム性の高い UDP を採用しつつ、TCP のような信頼性を確保することで、高速で信頼性の高い通信をトランスポート層で実現していること。
QUICが試験的に導入された
Node.js v15 では、この QUIC が試験的に導入されているのです。
注意点として、QUIC を Node.js v15 で使うためには、コンパイルオプションの「--experimental-quic」を設定しなければいけません。
また QUIC は、net モジュール内で実装されていて、以下のように利用することができます。
1const { createQuicSocket } = require('net');
V8が「V8 8.6」にアップデート
V8 は、ECMAScript に準拠した JavaScript エンジンのことで、Google が開発しているオープンソースです。
V8 は、以下のような特徴があります。
- Google Chrome や Node.js で利用されている
- C++ で書かれていて、C++ のアプリケーションにも組み込める
- 様々な OS で動作し、スタンドアローンでも動く
Node.js v15 では、利用されている V8 のバージョンが、V8 8.6 に更新されました。
ちなみに、Node.js v14 で利用されているバージョンは、V8 8.4 です。
V8 8.6 にアップデートされたことで、Node.js v15 では、以下の構文が利用できるようになっています。
- Promise.any()
- AggregateError
- String.prototype.replaceAll()
- &&=, ||=, ??=
では、ひとつずつ見ていきましょう。
Promise.any()
Promise.any() は、引数に与えられた複数の Promise を実行し、その中で一番早く resolve した結果を返します。
例えば、以下のようなコードを見てみましょう。
1const pErr = new Promise((resolve, reject) => {
2 reject("Always fails");
3});
4
5const pSlow = new Promise((resolve, reject) => {
6 setTimeout(resolve, 500, "Done eventually");
7});
8
9const pFast = new Promise((resolve, reject) => {
10 setTimeout(resolve, 100, "Done quick");
11});
12
13Promise.any([pErr, pSlow, pFast]).then((value) => {
14 console.log(value);
15})
16// 期待される出力: "Done quick"
pErr 、pSlow 、pFirst という3つの Promise オブジェクトがあります。
それぞれの Promise が実行完了する順番は、上から、
- pErr
- pFirst
- pSlow
の順です。
Promise.any() は、最初に resolve するまで、処理を続けます。
なので、pErr で最初に reject されたとしても、処理を継続するのです。
pFirst で resolve されると、Promise.any() の処理が完了します。
この場合、pSlow は実行されません。
つまり、コンソールには、「Done quick」と表示されるのです。
AggregateError
Promise.any() で、もし全ての Promise が reject されてしまった場合、どうなるのでしょうか?
このような場合、Promise.any() は、「AggregateError」を投げます。
AggregateError は、複数のエラーを、ひとつのエラーオブジェクトにまとめたものです。
Promise.any() のように、複数のエラーをまとめるときに用いられます。
1Promise.any([
2 Promise.reject(new Error("some error1")),
3 Promise.reject(new Error("some error2")),
4]).catch(e => {
5 console.log(e.errors); // [ Error: "some error1", Error: "some error2" ]
6});
このように AggregateError には、Promise.any() で reject された、全てのエラーがまとめられています。
String.prototype.replaceAll()
String.prototype.replaceAll() は、対象の文字列を「置換する文字列パターン」にマッチしたら、その全てを置換する関数です。
例えば、以下のように、特定の文字列をまとめて置換したいときに使います。
1'aaabbbccc'.replaceAll('b', '.');
2// 'aaa...ccc'
こちらは、従来からある replace 関数を使って書くと、次のようになりますね!
1'aaabbbccc'.replace(/b/g, '.');
2// 'aaa...ccc'
この例であれば、「replaceAll は、別に必要ないのでは?」と思う方も、中にはいるかと思います。
replaceAll は、正規表現でエスケープしなければいけない文字列(「+」や「.」など)が含まれている場合に便利です。
例えば、以下のように「.(ドット)」を replace で置換したいとき、誤ってエスケープし忘れた場合はどうなるでしょうか?
1'Number.prototype.toString'.replace(/./g, '->');
2'->->->->->->->->->->->->->->->->->->->->->->->->->'
正規表現でドットは、任意の文字列を指すので、全ての文字列が置き換わってしまいました…。
こうならないためにも、エスケープは忘れずにする必要があります。
ですが、replaceAll を使えば、そのようなことを考慮する必要はありません。
1'Number.prototype.toString'.replace(/\./g, '->');
2// 'Number->prototype->toString'
3
4'Number.prototype.toString'.replaceAll('.', '->');
5// 'Number->prototype->toString'
このように、replaceAll では、特殊な文字列でもエスケープせずに、マッチした全ての部分を置換できます。
「&&=」「||=」「??=」
こちらの3つは、「Logical assignment operators」と呼ばれるもので、それぞれの名前は次の通り。
- 論理積代入演算子:&&=
- 論理和代入演算子:||=
- Null 合体代入演算子:??=
ザックリと意味をまとめると、下表のようになります。
演算子 | 式 | 意味 |
&&= | x &&= y | x が、「truthy」の場合にのみ、y が代入 |
||= | x ||= y | x が、「falsy」の場合にのみ、y が代入 |
??= | x ??= y | x が、「null」または「undefined」である場合にのみ、y が代入 |
「npm7」の導入
npm の最新バージョン「npm7」が、10月12日にリリースされましたね!
その npm7 も、Node.js v15 に同梱されています。
npm7 の主な新機能は、以下のとおり。
- Workspace による Monorepo のサポート
- package-lock.json v2
- yarn.lock のサポート
- peer dependencies の自動インストール
Monorepo とは、単一のレポジトリで、複数のパッケージやモジュールを管理できる手法のことです。
Yarn では、以前からできていましたが、今回 npm 7 でも Monorepo を利用できるようになりました。
Node.js最新版を簡単に試してみるには?
Node.js の最新版を試すとき、公式サイトから最新版をダウンロードしてくる方法もあります。
ですが、もっと簡単に試したいですよね?
そんなときは、Docker を使って、サクッと試してみましょう!
Docker をインストールして、以下のコマンドを打てば、Node.js の最新版を試すことができます。
1$ docker run -it node
しばらくすると、以下のような表示が出てきます。
1Welcome to Node.js v15.0.1.
2Type ".help" for more information.
3>
ちゃんと Node.js v15 になっているようですね!
あとは、コードを入力すればいいので、試しに unhandled rejections も試してみましょう。
1> Promise.reject();
2Promise { <rejected> undefined }
3> node:internal/process/promises:218
4 triggerUncaughtException(err, true /* fromPromise */);
5 ^
6
7[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "undefined".] {
8 code: 'ERR_UNHANDLED_REJECTION'
9}
warning ではなく、エラーになっているのがわかると思います。
また、特定の Node のバージョンを指定して動かしたいときは、以下のようなコマンドで実行できます。
1$ docker run -it node:14
コロンの後ろにバージョンを指定することで、動かしたいバージョンを指定することが可能です。
ちなみに、ここでは v14 を指定しています。
さいごに
今回は、Node.js v15 の新機能、v14 以前からの変更点について見ていきました。
v15 では、unhandled rejections がエラーになるような大幅な変更から、QUIC など試験的に導入される先進的な機能までありましたね!
ぜひ、これを機に、最新技術に触れてみてください。
そして、自分の知識と Node のバージョンを、アップデートしていきましょう!
こちらの記事もオススメ!
2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
2020.07.28Java 特集実装編※最新記事順に並べています。Amazon EMRのHadoop完全分散モードクラスタ上でApache Spark...
ライトコードでは、エンジニアを積極採用中!
ライトコードでは、エンジニアを積極採用しています!社長と一杯しながらお話しする機会もご用意しております。そのほかカジュアル面談等もございますので、くわしくは採用情報をご確認ください。
採用情報へ
「好きを仕事にするエンジニア集団」の(株)ライトコードです! ライトコードは、福岡、東京、大阪、名古屋の4拠点で事業展開するIT企業です。 現在は、国内を代表する大手IT企業を取引先にもち、ITシステムの受託事業が中心。 いずれも直取引で、月間PV数1億を超えるWebサービスのシステム開発・運営、インフラの構築・運用に携わっています。 システム開発依頼・お見積もり大歓迎! また、現在「WEBエンジニア」「モバイルエンジニア」「営業」「WEBデザイナー」を積極採用中です! インターンや新卒採用も行っております。 以下よりご応募をお待ちしております! https://rightcode.co.jp/recruit