echo("備忘録");

IT技術やプログラミング関連など、技術系の事を備忘録的にまとめています。

【Node.js】ES2020仕様の便利な機能

はじめに

つい先日(2/3)、AWS LambdaがNode.js 14のサポートを開始しました。
AWS Lambda が Node.js 14 のサポートを開始

Node.js 10が4月末でEOL(新規Lambda作成は3月末くらいまで)になるので、このタイミングでNode.js 14(または12)への移行を検討している人もいるのでは?と思います。

そこで今回は、Node.js 14...というよりは、ES2020仕様(node.js 10ではほとんどが未サポート)で追加された、便利だなと思う新要素をピックアップしてみました。

ちなみに、各種要素のバージョン別サポート状況は、下記サイトで確認できます。
https://node.green/

紹介する新要素

  • Promise.allSettled
  • nullish coalescing operator (??)
  • optional chaining operator (?.)
  • (おまけ)CloudFormationでのデプロイ

Promise.allSettled(node.js 12.9.0以降)

これは前々回の【JavaScript】非同期処理(async/await)に関するちょっとしたTips - echo("備忘録");でも紹介した新要素です。

使い方としては下記ソースのようになり、引数の配列に指定した複数のPromiseについて、

  • 全Promiseの結果が返る(resolve/reject)されるまで待つ
  • 各Promiseについて、個別にresolve/rejectを判別できる

というものです。

Promise.all()と似ていますが「rejectされたPromiseがあっても、Promise.allSettled()自体はrejectされない(=エラーにならない)」点が異なります。

Promise.allSettled([...promise]).then(results => {});

具体的には、下記ケースで役に立ちます。

  • 複数の非同期処理を並列に実施する
  • 上記非同期処理が1つ以上rejectされるケースがおこりうる
  • 上記非同期処理がrejectされても、処理フロー自体はエラー扱いにしたくない

サンプルソース

/**
* Promise.allSettlednの使用例
*/  
const main = async () => {
  
    const promises = [];
    
    for(let i = 0; i <3; i++) {  
        // someFuncAsync()は何か処理を行う非同期処理。  
        // ただし、処理フロー上rejectされるケースが  
        // 普通に起こりえるとする。
        promises.push(someFuncAsync(i));
    }
  
    // Promise.allSettledで全Promiseを待つ
    const results = await Promise.allSettled(promises);
    
    // ここからが異なる
    for (let j = 0; j < 3; j++) {
  
       // Promise.allSettled()を使用した場合、各promiseの  
       //「status」キーでresolve/rejectを判別可能  
       // もちろんrejectされても、Promise.allSettled()自体は
       // rejectされない。(=エラーにならない) 
        if (results[j].status === 'fulfilled') {  
            // statusが'fullfilled'の場合、そのpromiseはresolveされた。 
            // その場合、resolveされた値はキー「value」に格納される
            console.info(`someFuncAsync(${j}) はresolveされました。`);
            console.info(`戻り値: ${results[j].value}`);
        } else if (results[j].status === 'rejected'){  
            // statusが'rejected'の場合、そのpromiseはrejectされた。  
            // その場合、キー「reason」にその理由が格納される
            console.warn(`someFuncAsync(${j}) はrejectされました。`);  
            console.warn(`理由: ${results[j].reason}`);
        }
    }
}  

nullish coalescing operator (??) (node.js 14.0.0以降)

日本語に訳すと「null合体演算子」(?)

使い方としては、下記ソースのような感じです。

const hoge = fuga ?? piyo;

上記ソースですが、挙動として

  • fugaがnullまたはundefinedの場合のみ、??の右の値(piyo)を返す。
    • hoge=piyoになる
  • fugaがnullでもundefinedでもない場合、??の左の値(fuga)をそのまま返す。
    • hoge=fugaになる

となります。

今までこういう場合、下記ソースのように「||」演算子で比較していましたが、「??」は「||」と違い、fugaが空文字、0、falseでもhoge=fugaになるという違いがあります。

というか、この点が非常に大きくて、結構これがバグの原因になったり、制御が厄介だったりしたので、個人的にこの機能の追加追加は本当に嬉しいです。

// 「||」での判定では、fugaが空文字, 0 ,falseだとhoge = piyoになる。 
const hoge = fuga || piyo;  
  
// 「??」での判定では、fugaが空文字, 0 ,falseでもhoge = fugaになる。 
const hoge = fuga || piyo;  

optional chaining operator (?.) (node.js 14.0.0以降)

日本語に訳すと「選択的連結演算子」(かな?)

使い方としては、下記ソースのような感じで「nullまたはundefinedになるかもしれない要素、およびその子孫要素にアクセスする」場合に使用します。

const hoge = fuga?.piyo

上記ソースは、下記の挙動となります。

  • fugaがnullまたはundefinedの場合、hoge=undefinedになる。
  • fugaがnullでもundefinedでもない場合、hoge = fuga.piyoになる。
ネストされたオブジェクトへのアクセスに便利

これが便利なのが、上記に書いた通り「ネストされたオブジェクトへのアクセス」で、例えば下記オブジェクト(何かしらのユーザーの指名情報)があったとします。

そして「fullName以降のキーは指定されないケースもある」とします。

const userData = {
    user: {  
        country: 'USA',
        fullName: {
            firstname: 'John',
            middleName: 'Fitzgerald', 
            familyName : 'Kennedy' 
    }
}

この場合に「firstName」にアクセスする場合、今までだと下記のように、親以上の要素のキーについてチェックする必要がありました。

const firstName = (userData.user.fullName && userData.user.fullName.firstName) ? userData.user.fullName.firstName : "No firstName";  
  
// 下記だとfullNameがない場合にエラーになる  
const firstName = userData.user.fullName.firstName;  

しかし今回のoptional chaining operatorを使えば、下記1行でOKになります。

// 下記だとfullNameがなくてもエラーにならない。  
// fullNameがない場合、firstNameはundefinedになる。  
const firstName = userData.user.fullName?.firstName;  
  
// nullish coalescing operatorと組み合わせて、下記書き方もできる。  
const firstName = userData.user.fullName?.firstName ?? "No firstName";  

(おまけ)CloudFormationでのデプロイについて

AWS Lambdaのランタイムをnode.js 14にしてCloudFormationでデプロイする場合ですが、AWS公式のドキュメントの「Runtime」項目には「node.js 14.x」の記載がありません。

docs.aws.amazon.com

しかし、これは単にドキュメントの修正がまだされてないだけで、Runtimeに「node.js14.x」を指定すれば、問題なくnode.js 14でのデプロイが可能です。

また、Serverless Frameworkでもprovider.runtimeに「node.js14.x」を指定すれば、問題なくnode.js 14でのデプロイが可能です。(下記画像の警告は出ますが、デプロイ自体は問題なし)
f:id:Makky12:20210211191551p:plain

まとめ

以上、Node.js 14(てか、ES2020仕様)で便利な機能の紹介でした。

中でもPromise.allSettled()やnullish coalescing operatorなんかは、本当に便利だと思いますので、こういう便利な新機能をどんどん活用していきたいですね。

というか既存機能でも結構知らない機能があったので、node.green なり MDN Web Docs なりでしっかり見直しておかないといけないなあ、と思いました。

告知

3/20(土)に開催される「JAWS DAYS 2021」にて、「AWS Lambdaのテストで役立つ各種ツール」という内容で登壇させていただくことになりました。(諸事情により、タイトル変更しました)
jawsdays2021.jaws-ug.jp

内容としては、1/6(水)の「Serverless Meetup Japan Virtual #14」での登壇内容である「Severless Frameworkで行うLambdaテスト」をベースに、時間の関係でお話しできなかった内容を追加する予定です。

それでは、今回はこの辺で。