MicrosoftのNode.jsチュートリアルに取り組んでいる

learn.microsoft.com

ちょっとしたアプリケーションがほしいなと思っていて、勉強がてらTypeScriptとかNode.jsを使ってみよう→まずはチュートリアルで感覚を掴もう、と信頼できそうなサイトを見つけたが、これがまた曲者でなかなか前に進まず苦労している。

動作環境

閲覧ページなどの情報はすべて執筆時点のもの。

Node.jsの動作の仕組み

learn.microsoft.com

序盤は問題なく動作する。しかし「最上位の async/await」でpackage.jsonを追加すると、その後のプログラムが動かなくなる。

ReferenceError: require is not defined in ES module scope, you can use import instead

どうやら "type":"module" を指定すると require が使えないらしい。メッセージ通り import を使う方法に切り替えた。

// サンプルコードのrequireを止める
// const fs = require('fs').promises;
// importに置き換える
import * as fs from 'node:fs/promises';

"type":"module" を指定し続ける限り同じ問題が発生するので、その後のサンプルコードも同様の処置が必要。あるいはディレクトリを分けるとか、package.jsonを削除するといった対応でも動作する。多分 "type":"module" が実際の開発のスタンダードだろうから、import を使った方がいいんだろうなという気はする。

Node.js を試す

REPLやHello, worldは問題なく動く。しかしその次の「非同期関数を追加する」が恐らく間違っている。

前述の通りトップレベルで await を使うためにはpackage.json"type":"module" の指定が必要で、それをやると require が使えなくなるので import に置き換えなければならない。しかしこのサンプルコードは置き換えてもきちんと動作せず、実行するとステータスコード(200)が表示されるべきところが undefined になる。

公式リファレンスにしたがってコールバック関数を渡すと問題なく動作する。

HTTPS | Node.js v20.12.2 Documentation

※ ページ内リンクがうまく働いてくれない……。何回か読み込み直すと良い感じに表示される時もある

コールバック関数なら動くがトップレベルの await では動かない。ついでに単純なasync-await構文でも動かない。

いろいろ試した結果、await を用いた場合の戻り値は ClientRequest 型で、この中に statusCode プロパティは存在しないことが判明した。 Request なんだからそりゃそうだよな。

もう一度公式リファレンスに戻って、今度は ClientRequest の説明を読む。

HTTP | Node.js v20.12.2 Documentation

To get the response, add a listener for 'response' to the request object. 'response' will be emitted from the request object when the response headers have been received. The 'response' event is executed with one argument which is an instance of http.IncomingMessage.

レスポンスを受け取るには response イベントに対してリスナーを設定する必要がある……らしい。説明全体を読んでも Promise がどうこうといった話は出てこない。

まさかそんなことはないだろうと思いたかったが、どうやらそもそもの話、このサンプルコード全体が間違っていると結論づけるしかなかった。まさか天下のマイクロソフト様が作られたチュートリアルでそんなことがあろうとは。今でも半信半疑なので、私の勘違いを指摘してくれるコメントを待っています。

ということで「非同期関数を実装する」のサンプルコードは、非同期関数を使わずに実装しないと動かない。かもしれない。

import * as https from 'node:https';

console.log(`start`);

https.get('https://nodejs.org/dist/index.json', res => {
    console.log(res.statusCode);
});

console.log(`end`);

await を使わないので require のままでもいいんだけど、基本的に "type":"module" で進めることに決めたのでそこだけは直していない。

チュートリアルだから、サンプルコードだから、と流さずひとつひとつの問題と向き合うことで知識は深まったが、それにしてもこんな誤りが……あるのか……?と信じがたい気持ちは拭えない。上記2つは両方ともフィードバックを送信したので、運が良ければいつか直っている、かもしれない。