2020年8月7日金曜日

Node.jsでよく使う書き方まとめ

最近 Node.js をメインにし始めてみてますが、あまり使い方がまとまってないので、自分用にまとめてみました。 これだけわかれば、普通の言語並に使いやすいんじゃないかな。 よく使うとこだけのまとめなので、困った時は公式のリファレンスを見ましょう。



sprintf

var util = require('util');
util.format('hello %s', 'world');
console.log('hello %s', 'world');

// npm install printf
var printf = require('printf');
var result = printf(format, args...);

// on Browser (上を後述の方法で野良ビルドしても良い)
// https://github.com/alexei/sprintf.js

printf (改行なし)

process.stdout.write("hello: ");

コマンドライン引数

// argv[0] = node, argv[1] = 'test.js', argv[2] = ...
let args;
if (process.argv.length < 3) {
  console.log('USAGE: test.js [file]');
  process.exit(1);
}
args = process.args.slice(2);
for (var i = 0; i < process.argv.length; i++){
  console.log(process.argv[i]);
}

sleep

Node.js で真っ先にハマるのは sleep でしょう。 Promise/async/await でも良いんだけど for 文でネスト地獄になり、また事故りやすいです。 色々な書き方があるけど、事故のないシンプルな時間 sleep が一番良いと思ってます。
function sleep(time) {
  const d1 = new Date();
  while (true) {
    const d2 = new Date();
    if (d2 - d1 > time) {
      return;
    }
  }
}

ファイル読み込み (一度に全部)

const fs = require("fs");
fs.readFile("input.txt", 'utf-8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

ファイル読み込み (複数を同期的に1行ずつ)

このコードで重要なのは、top-level で await しようとするとできない (Node.js v14.8.0 からは"モジュールなら" できる、そしてここがまた新たなトラップでもある…) ので、無名関数でラップしているということです。 私は top-level await が効かないのが嫌で、同期的に呼び出したい時は適当なライブラリを使ったりしていました。 ただ Deno に完全移行するに当たって、Node.js の遺産は Deno の書き方に近い、以下のコードで統一することにしました。 await で同期化したり、EOF チェックでバグを起こしにくくしているのがポイントです。 個人的バグ踏みやすさ No.1 は間違いなくこれ。
const fs = require("fs");
const readline = require("readline");

async function main() {
  const stream = fs.createReadStream(file);
  const reader = readline.createInterface({ input: stream });
  for await (const line of reader) {
    if (!line) continue;
    console.log(line);
  }
}
await main();

encoding

ファイル単位の同期処理、行単位の同期処理、そしてそこに encoding が入ってくると大変なことになる。 たぶんだけど行単位で shift-jis → utf8 に変換しながら同期的に処理はできない。 今のとこ対応してる標準ライブラリはない感じに見える。 たいていのケースは手動で事前変換して処理したほうが良さそう。 外部ライブラリなら、私は polygonplanet/encoding.js を使うことが多いです。

ファイル書き込み

バッファリングなど他の書き方はこちら
const fs = require('fs');
fs.writeFileSync("output.txt", "abc");
fs.writeFileSync("output.txt", "abc", (err) => {
  if (err) throw err;
  console.log('OK');
});

ファイルとディレクトリの判別

const fs = require('fs');
fs.stat('/dir/path', function (err, stats) {
  if (err) throw err;
  if (stats.isDirectory()) {
    console.log('This is a directory');
  }
});

glob

// npm install glob
const glob = require("glob")

// options is optional
glob("**/*.js", options, function (err, files) {
});

時間計測

console.time('hoge');
// 処理
console.timeEnd('hoge');

シェル実行 (非同期)

const exec = require('child_process').exec;
exec('ls -la', (err, stdout, stderr) => {
  if (err) { console.log(err); }
  console.log(stdout);
});

シェル実行 (同期)

const execSync = require('child_process').execSync;
var stdout = execSync('ls -la');  // Buffer なので通常は toString() が必要
console.log(`stdout: ${stdout}`);

野良ビルド

// npm install esbuild  // ビルドツール (webpack, etc.)
// npm install mnemonist  // 使いたいライブラリ
./node_modules/.bin/esbuild --bundle --minify build.js > bit-vector.js
build.js では使いたいライブラリのモジュールを require するだけ。
// npm install esbuild
(function() {
  var BitVector = require('mnemonist/bit-vector');  // 使いたいモジュール
})();

ファイルの扱い、async/await の書き方 (私も苦手なのでまとめた)、そしてビルドにさえ慣れてしまえば、あまりハマるところはなく使いやすい。 ただ Deno と比較するとだいぶ使いにくいさを感じるようにもなりました。

0 件のコメント: