Node.jsでprompt()

top-image

メッセージを表示してユーザーに入力を促し、入力された文字列を返す。

ブラウザならprompt()で造作もなくできることだが、Node.jsにはそういう機能はない。調べてみると、Readlineという標準モジュールを使ってほぼ同じことができるようなのでやってみた。

Readlineモジュールには2種類のAPIがある。Promiseを返すか、コールバックを呼ぶか。ここではPromiseを返す方を使う。

// Promiseを返す方のReadlineをreadlineという名前で読み込む
import { promises as readline } from 'node:readline';

const prompt = async(ask)=>{
	const ins = readline.createInterface({ // インスタンスの作成(入力待受開始)
		input: process.stdin, // どこからの入力を待ち受けるか(必須)
		output: process.stdout // 入力されたデータをどこに書き込むか
	});
	return await ins.question(ask) // 引数の文字列を表示し、入力された文字列を返すメソッド
	.then((answer)=>{
		ins.close(); // インスタンスを閉じる(入力待受終了)
		return answer;
	});
};

const name = await prompt('Input your name.\n');
process.stdout.write(`\x1b[32m${name}\x1b[39m\n`);

question()の機能に着目すると、これだけでprompt()と同じことができそうに思える。でもそれだとうまくいかない。なぜなら、インスタンスを作成したあとclose()されるまでは、ユーザーの入力を待ち受ける状態が解除されないからだ。

ちなみにquestion()は第二引数にAbortSignalを渡せるので、入力時間を制限したりとかもできる。

const prompt = async(ask)=>{
	const signal = AbortSignal.timeout(5000); // このメソッドはタイムアウトを設定した新しいAbortSignalを返す
	signal.onabort = ()=>{ // abortされたとき何かする場合は、これの他にaddEventlistener()を使う方法もある
		console.log('too late!');
	};
	const ins = readline.createInterface({
		input: process.stdin,
		output: process.stdout
	});
	return await ins.question(ask, { signal: signal }) // 第二引数はオブジェクト
	.then((answer)=>{
		ins.close();
		return answer;
	})
	.catch(e=>{
		ins.close();
		if(e.code === 'ABORT_ERR') { // タイムアウトとはいえabortはエラーなので例外処理は必要
			return 'what a slowpoke you!';
		}
	});
};

prompt()もだが、Readlineは文字通り1行を読むための機能。返ってきた文字列をファイルに書き出すときなど、改行を反映するのがやや面倒。これが理由でこの案は使わなかった。でもちょっとした入力だけでいいときは便利そうなので、たぶんいつか使うだろう。

お知らせ

現在お知らせはありません。

最近の投稿

シェア