EY-Office ブログ

for文とforEach関数は等価ではない、非同期処理においては

みなさんは、forEach関数(メソッド)は好きですか? 私は昔LispをやっていたこともありforEachやmap関数が大好きです。for文はめったに使わないです。

const a = [1, 3 ,5 ,7];
for (const n of a) {
  console.log(n * 3);
}

const a = [1, 3 ,5 ,7];
a.forEach(n => console.log(n * 3));

は等価です。

Matrix

もう1つ

const a = [1, 3 ,5 ,7];
let total = 0;
for (const n of a) {
  total += n;
}
console.log(total)

const a = [1, 3 ,5 ,7];
let total = 0;
a.forEach(n => total += n);

console.log(total);

は等価です。しかし、これはreduce関数を使うべきですね。

const a = [1, 3 ,5 ,7];

console.log(a.reduce((s, n) => s + n), 0);

node.jsでハマった

ある機能をnode.jsで作ろうとした時に、いつものようにforEach関数を使ってコードを書いてハマってしまいました。解決できたあとで考え直してみると当たり前なのですが同じようにハマる人もいそうなので書いてみます。

説明の都合で単純な例にしました、

  1. sqls配列にSQL文の文字列が入っています
  2. pg-promiseのnone()関数を使い、先頭から順番に実行します
    • pg-promiseはPostgreSQLに接続しSQL文を実行するライブラリーです
    • pg-promiseは非同期処理、Promise、async/awaitで処理を行います
const sqls = ["CREATE TABLE users(id SERIAL PRIMARY KEY, name VARCHAR(255))",
              "INSERT INTO users(name) VALUES('山田太郎')"];
const db = ...省略...;  // pg-promiseの初期化とデータベースをアクセスするインスタンスを作成
sqls.forEach(async(sql) => {await db.none(sql)});

と書いて、実行するとerror: relation "users" does not existエラーになってしまいました。

なぜ、これがエラーになるかというとasync(sql) => {await db.none(sql)}無名関数内ではdb.none(sql)の実行はawaitで終了を持ちますが、この無名関数自体はPromiseを作成しPostgreSQLにSQL文を送信したら直ぐに終了してしまいます。

したがってCREATE TABLE文の実行が終了する前に、INSERT INTO文の実行が始まってしまいます。そしてINSERT INTO文の実行時点ではusersテーブルが無いのでエラーになってしまうのです。

解決策

pg-promiseのTaskにはbatch処理などもあり色々と試してみましたが解決しませんでしたが、一晩寝たら良い解決方法が思い浮かびました! for文を使えば良いのです😁

const sqls = ["CREATE TABLE users(id SERIAL PRIMARY KEY, name VARCHAR(255))",
              "INSERT INTO users(name) VALUES('山田太郎')"];
const db = ...省略...;  // pg-promiseの初期化とデータベースをアクセスするインスタンスを作成
(async() => {
  for (sql of sqls) {
    await db.none(sql)
  }
})();

このコードではsqlsの1つの要素のSQL実行が終了してから次の要素の実行に移るので、上のようなエラーは発生しなくなりました。

まとめ

for文とforEach関数は同期処理の世界では等価ですが、非同期処理の世界では等価にならない事があります。
コードを書いていると、いつも得意なパターンを使って書いてしまいがちです。しかし世の中には等価のコードのパターンもあるので、それらを知り、対象のプログラムに最適なコードを選択できることが重要だと思いました。

映画マトリクスの続編が公開されるようですね楽しみです! そこで今回の画像はマトリクス風にしてみました。

- about -

EY-Office代表取締役
・プログラマー
吉田裕美の
開発者向けブログ