Node.js はシングルスレッドで実行されるので,排他処理は原則不要ですが,非同期関数が含まれるとその限りではないと思ったので,排他処理ができるライブラリを探したのですが,自分で作った方が簡単だと思ったので,実験してみました.
以下のソースコードは全て TypeScript の Vitest 用単体テストです.
排他処理が必要になる例として,変数の値を取得して,非同期的に更新する操作を実装します.
lock.spec.ts
import { assert, test } from "vitest"; test("once", async () => { let v = 0; const addOne = async () => { const t = v; await new Promise(resolve => setTimeout(resolve, 10)); // sleep 10 ms v = t + 1; }; await addOne().catch(() => { }); assert.equal(v, 1); }); test("twice without lock", async () => { let v = 0; const addOne = async () => { const t = v; await new Promise(resolve => setTimeout(resolve, 10)); // sleep 10 ms v = t + 1; }; await Promise.all([addOne(), addOne()]).catch(() => { });; assert.equal(v, 1); }); test("twice with lock", async () => { let v = 0; let lock = false; const addOne = async () => { while (lock) { await new Promise(resolve => setTimeout(resolve, 10)); // sleep 10 ms } lock = true; const t = v; await new Promise(resolve => setTimeout(resolve, 10)); // sleep 10 ms v = t + 1; lock = false; }; await Promise.all([addOne(), addOne()]).catch(() => { }); assert.equal(v, 2); });
更新処理を非同期で実行する前に lock
変数を確認して,false になるまでまち続ける動作です.
実行したところ,全て合格しました.
✓ tests/lock.spec.ts (3) ✓ once ✓ twice without lock ✓ twice with lock Test Files 1 passed (1) Tests 3 passed (3) Start at 21:06:50 Duration 53ms
マルチスレッドならば上記の例では不足しますが,Node.js はシングルスレッドで実行されるので問題ないようです.