メッセージを表示してユーザーに入力を促し、入力された文字列を返す。
ブラウザなら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行を読むための機能。返ってきた文字列をファイルに書き出すときなど、改行を反映するのがやや面倒。これが理由でこの案は使わなかった。でもちょっとした入力だけでいいときは便利そうなので、たぶんいつか使うだろう。
0 件のコメント:
コメントを投稿