• トップ
  • ブログ一覧
  • 10月20日メジャーアップデート!「Node.js v15」の新機能とは?
  • 10月20日メジャーアップデート!「Node.js v15」の新機能とは?

    広告メディア事業部広告メディア事業部
    2021.03.29

    エンジニアになろう!

    10月20日にメジャーアップデートされた「Node.js v15」について解説

    2020年10月20日に、Node.js のメジャーアップデートがありました。

    というわけで、バージョン15 が、遂にリリースされました!

    このアップデートにより、前のバージョンから変更があったり、実験的に新しい機能が追加されていたりします。

    Node.js は、半年ごとにアップデートされていますが、「最新版がどういったものなのか、よくわからない」という方が多いのではないでしょうか?

    この記事では、

    1. 「Node.js v15」で導入された機能
    2. 前バージョンからの変更点

    について、詳しく解説していきたいと思います!

    Node.js v15とは?

    「Node.js v15」は、2020年10月20日にリリースされた、Node.js の最新バージョンです。

    (※2020年12月時点)

    ちなみにこのバージョンは、「15」という数字が入っていることから、奇数バージョンと呼ばれています。

    サポート期間は、比較的短いですが、最新の機能が導入されてるのが特徴です。

    つまり、最新の機能を、先取りすることができるわけですね!

    Node.js v15とは?

    では次から、いよいよ「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 とは、

    1. reject された Promise
    2. 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 は、以下のような特徴があります。

    1. Google Chrome や Node.js で利用されている
    2. C++ で書かれていて、C++ のアプリケーションにも組み込める
    3. 様々な OS で動作し、スタンドアローンでも動く

    Node.js v15 では、利用されている V8 のバージョンが、V8 8.6 に更新されました。

    ちなみに、Node.js v14 で利用されているバージョンは、V8 8.4 です。

    V8 8.6 にアップデートされたことで、Node.js v15 では、以下の構文が利用できるようになっています。

    1. Promise.any()
    2. AggregateError
    3. String.prototype.replaceAll()
    4. &&=, ||=, ??=

    では、ひとつずつ見ていきましょう。

    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"

    pErrpSlowpFirst という3つの Promise オブジェクトがあります。

    それぞれの Promise が実行完了する順番は、上から、

    1. pErr
    2. pFirst
    3. 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」と呼ばれるもので、それぞれの名前は次の通り。

    1. 論理積代入演算子:&&=
    2. 論理和代入演算子:||=
    3. Null 合体代入演算子:??=

    ザックリと意味をまとめると、下表のようになります。

    演算子意味
    &&=x &&= yx が、「truthy」の場合にのみ、y が代入
    ||=x ||= yx が、「falsy」の場合にのみ、y が代入
    ??=x ??= yx が、「null」または「undefined」である場合にのみ、y が代入

    「npm7」の導入

    npm の最新バージョン「npm7」が、10月12日にリリースされましたね!

    その npm7 も、Node.js v15 に同梱されています。

    npm7 の主な新機能は、以下のとおり。

    1. Workspace による Monorepo のサポート
    2. package-lock.json v2
    3. yarn.lock のサポート
    4. 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 のバージョンを、アップデートしていきましょう!

    こちらの記事もオススメ!

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...
    featureImg2020.07.28Java 特集実装編※最新記事順に並べています。Amazon EMRのHadoop完全分散モードクラスタ上でApache Spark...

    広告メディア事業部

    広告メディア事業部

    おすすめ記事