「処理回数制限」ユーティリティは何を守るためのものか
「処理回数制限」は、
「この関数は最大〇回までしか動かさない」
というルールをコードで表現するためのユーティリティです。
例えば次のような場面を想像してみてください。
ボタン連打で同じ処理が何度も走ってしまう
リトライ処理が暴走して、延々と API を叩き続けてしまう
ログ出力がループの中で何千回も呼ばれてログがあふれる
こういう「やりすぎ」を防ぐために、
「この処理は多くても N 回まで」という“上限”をつけるのが「処理回数制限」です。
基本形:最大 N 回までしか動かない関数を作る
createLimited 関数の実装
まずは、いちばん素直な形から作ってみます。
function createLimited(fn, maxCalls) {
let count = 0;
return function limited(...args) {
if (count >= maxCalls) {
return;
}
count += 1;
return fn(...args);
};
}
JavaScriptこの createLimited は、
元の関数 fn
最大実行回数 maxCalls
を受け取って、
「呼ばれても、実際に中身が動くのは最大 maxCalls 回まで」
という関数を返します。
使い方の例を見てみましょう。
function greet(name) {
console.log(`こんにちは、${name} さん`);
}
const greetUpTo3 = createLimited(greet, 3);
greetUpTo3("太郎"); // 実行される(1回目)
greetUpTo3("花子"); // 実行される(2回目)
greetUpTo3("次郎"); // 実行される(3回目)
greetUpTo3("三郎"); // 何も起きない(4回目なので無視)
JavaScriptここでの重要ポイントは二つです。
内部で count を持っていて、呼ばれるたびに増やしているcount >= maxCalls になったら、それ以降は fn を呼ばない
つまり、「何回呼ばれたか」をクロージャで覚えておき、
上限を超えたら静かに無視する、という仕組みになっています。
クロージャで「回数の状態」を閉じ込める意味
createLimited の中で定義されている count は、
外から直接触ることができません。
function createLimited(fn, maxCalls) {
let count = 0; // 外からは見えない
return function limited(...args) {
// ここからだけ触れる
};
}
JavaScriptこの「外から見えないけど、中の関数だけが覚えている状態」がクロージャです。
これによって、
誰かがうっかり count = 0 に戻してしまう
別の処理から count を勝手に増やしてしまう
といった事故を防げます。
業務コードでは、
「制御に使う状態は、できるだけ外から触れないように閉じ込める」
というのがとても大事な考え方です。
処理回数制限ユーティリティは、その良い練習になります。
「上限に達したこと」を呼び出し側に知らせる
実行されたかどうかを返り値で知りたい場合
さっきの createLimited は、上限を超えたとき「何も返さない」だけでした。
場合によっては、「もう実行されなかった」と分かった方が便利なこともあります。
その場合は、結果と一緒に「実行されたかどうか」を返すようにします。
function createLimitedWithFlag(fn, maxCalls) {
let count = 0;
return function limited(...args) {
if (count >= maxCalls) {
return { executed: false, result: undefined };
}
count += 1;
const result = fn(...args);
return { executed: true, result };
};
}
JavaScript使い方はこうです。
function add(a, b) {
return a + b;
}
const addUpTo2 = createLimitedWithFlag(add, 2);
console.log(addUpTo2(1, 2)); // { executed: true, result: 3 }
console.log(addUpTo2(3, 4)); // { executed: true, result: 7 }
console.log(addUpTo2(5, 6)); // { executed: false, result: undefined }
JavaScriptこれで、呼び出し側は
実際に処理が走ったのか
もう上限に達して無視されたのか
を判別できるようになります。
非同期処理の回数制限
async 関数にも同じ考え方を適用する
API 呼び出しなど、非同期処理にも回数制限をかけたいことがあります。
その場合も、基本の考え方は同じです。
function createLimitedAsync(fn, maxCalls) {
let count = 0;
return async function limited(...args) {
if (count >= maxCalls) {
return { executed: false, result: undefined };
}
count += 1;
const result = await fn(...args);
return { executed: true, result };
};
}
JavaScript使い方の例です。
async function fetchUser(id) {
const res = await fetch(`/api/users/${id}`);
return res.json();
}
const fetchUserUpTo3 = createLimitedAsync(fetchUser, 3);
await fetchUserUpTo3(1); // 実行される
await fetchUserUpTo3(2); // 実行される
await fetchUserUpTo3(3); // 実行される
await fetchUserUpTo3(4); // executed: false になる
JavaScriptこれで、「この API は最大 3 回までしか叩かない」といった制御が簡単に書けます。
実務での具体的な利用イメージ
ボタン連打による多重実行を防ぐ
例えば、「送信」ボタンを連打されると同じ処理が何度も走ってしまう画面を考えます。
function sendForm() {
console.log("送信処理を実行");
}
const sendFormLimited = createLimited(sendForm, 1);
// ボタンのクリックハンドラ
function onClick() {
sendFormLimited();
}
JavaScriptこれなら、ユーザーがボタンを何度押しても、
実際に送信処理が走るのは最初の 1 回だけです。
UI 側でボタンを無効化するのと合わせて使うと、
多重送信の事故をかなり防げます。
再試行処理の「保険」として
再試行ユーティリティと組み合わせて、
「そもそもこの処理全体を最大 N 回まで」と制限することもできます。
const runTaskLimited = createLimitedAsync(runTask, 5);
await runTaskLimited(); // 最大5回までしか呼ばれない
JavaScriptこれにより、「どこかのバグで同じ処理が何度も呼ばれ続ける」ような事故に対して、
一つの“安全弁”を用意できます。
「処理回数制限」と「一度だけ実行」の違い
以前出てきた「一度だけ実行(once)」と、今回の「処理回数制限」はよく似ていますが、役割が少し違います。
一度だけ実行
最大 1 回まで
主に「初期化」「イベント登録」など、一回だけ許可したい処理向け
処理回数制限
最大 N 回まで(N は自由に決められる)
「多重実行を防ぎたい」「リトライ回数を制限したい」など、柔軟な制御向け
実装もほぼ同じパターンなので、
「once を N 回版に一般化したものが処理回数制限」と考えてもらって大丈夫です。
小さな練習で感覚をつかむ
最後に、次のようなコードを自分で書いて動かしてみてください。
function logHello() {
console.log("Hello");
}
const logHelloUpTo2 = createLimited(logHello, 2);
logHelloUpTo2(); // Hello
logHelloUpTo2(); // Hello
logHelloUpTo2(); // 何も出ない
logHelloUpTo2(); // 何も出ない
JavaScriptそのうえで、
最大 3 回版
最大 5 回版
非同期版(createLimitedAsync)
などを自分で作ってみると、
「回数を状態として持ち、それを条件に処理を制御する」という感覚がしっかり身についてきます。
処理回数制限ユーティリティは、
「やりすぎないためのブレーキ」をコードに埋め込むための道具です。
一度自分のプロジェクトに入れておくと、「あ、ここは上限を決めておきたいな」という場面で、すぐに使い回せるようになります。
