JavaやC++などではマルチスレッドプログラミングではセマフォを使った実装はよくありますが、JavaScriptでは以下のようなケースでセマフォを利用したくなることがあります
async-sema はJavaScriptのasync-awaitでセマフォを実装したライブラリです。
以下の例は20個分の要素(データ)を最大3つずつ同時に処理する例です
const { Sema } = require("async-sema");
const wait = () => new Promise((resolve) => setTimeout(resolve, 1000));
async function main() {
const items = [];
for (let i = 0; i < 20; i++) items.push(i + 1);
const s = new Sema(3);
await Promise.all(
items.map(async (elem) => {
await s.acquire();
console.log(elem, s.nrWaiting());
await wait();
s.release();
})
);
console.log("done");
}
main();
要点は3つで
new Sema()
でセマフォを定義。引数は同時に処理する数を指定するs.acquire()
でセマフォからトークンを取得。利用可能なリソースを減らす(デクリメント(P操作))する。s.release()
でセマフォを開放し、利用可能なリソースを増やす(インクリメント(V操作))全体をPromise.allでラップします。
s.nrWaiting()
で残りの処理数を出力します。
コンストラクタで用意されている引数のオプションには以下の物があります
initFn
セマフォのトークンを管理する場合に利用します。デフォルトではトークは'1'(() ⇒ '1'
)に設定されています。pauseFn
リクエストを一時的に中断する関数を指定することができますresumeFn
リクエストを再開する関数を指定することができますcapacity
セマフォに割り当てる処理のリストがいくつあるかを指定します。通常はパフォーマンス向上のため利用されるdrain()
セマフォをドレインし、初期化されたすべてのトークを配列で返します。プロセスを終了する前など、保留中の非同期タスクがないかを確認する事ができます。tryAcquire()
トークンが利用可能かを確認する、利用できない場合はundefinedが返る。