「非同期関数の再利用」を一言でいうと
非同期関数の再利用は、
「一度作った Promise ベースの処理を、“場面だけ変えて何度も使い回せる形” に設計すること」
です。
言い換えると、
同じような API 呼び出しや、同じようなエラーハンドリングを
毎回コピペで書くのではなく、
「1 回ちゃんと関数にして、“パラメータだけ変えて使う”」 状態まで持っていくことです。
ここが重要です。
Promise の世界では、
「Promise を返す関数」=「再利用できる非同期部品」
と考えて設計していくと、とても扱いやすくなります。
再利用しやすい「Promise を返す関数」の基本形
まずは「生 Promise」を書かず、関数に包む
初心者が最初にやりがちなのが、
ファイルのトップレベルにいきなり new Promise(...) を書いてしまうことです。
// あまり良くない例(その場かぎり)
const p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("データ");
}, 1000);
});
p.then((data) => {
console.log(data);
});
JavaScriptこれだと、「1 回きり」で終わります。
もう一度同じ処理をしたいときは、また new Promise を書き直すことになります。
再利用したいなら、
「Promise を返す関数」にしておく のが基本です。
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("データ");
}, 1000);
});
}
// 何度でも使える
fetchData().then((data) => console.log("1回目:", data));
fetchData().then((data) => console.log("2回目:", data));
JavaScript引数で「やりたいこと」を変えられるようにする
さらに再利用度を上げるには、
「引数で違いを表現できるようにする」 のがポイントです。
function delay(ms, value) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(value);
}, ms);
});
}
// いろいろ使える
delay(1000, "A").then((v) => console.log("1秒後:", v));
delay(2000, "B").then((v) => console.log("2秒後:", v));
JavaScriptdelay というたった 1 つの関数で、
「何秒後に何をしたいか」を変えられます。
ここが重要です。
「再利用可能な非同期関数」は、
1回限りの Promise ではなく、
“引数を変えて何度でも呼べる Promise を返す関数”として設計する
ところから始まります。
API 呼び出し処理を「再利用できる形」にする
悪い例:毎回、直書きの fetch
例えば、画面のあちこちでこう書いているとします。
// 画面A
fetch("/api/user")
.then((res) => res.json())
.then((data) => console.log("A で使う:", data))
.catch((err) => console.error(err));
// 画面B
fetch("/api/user")
.then((res) => res.json())
.then((data) => console.log("B で使う:", data))
.catch((err) => console.error(err));
JavaScript完全に同じことを、場所だけ変えて書いています。
コピペが増えると、
エラーハンドリングを変えたいときに全部直さないといけない、という地獄になります。
良い例:共通の「非同期関数」に切り出す
これを「再利用可能な非同期関数」にしましょう。
function fetchJson(url) {
return fetch(url).then((res) => {
if (!res.ok) {
throw new Error(`HTTP エラー: ${res.status}`);
}
return res.json();
});
}
JavaScript使う側はこうなります。
// 画面A
fetchJson("/api/user")
.then((user) => {
console.log("A で使う:", user);
})
.catch((err) => {
console.error("A でのエラー:", err);
});
// 画面B
fetchJson("/api/user")
.then((user) => {
console.log("B で使う:", user);
})
.catch((err) => {
console.error("B でのエラー:", err);
});
JavaScript共通化したことで、
HTTP ステータスのエラーチェック.json() のパース
など、「どの画面でも共通な処理」を関数の中に閉じ込められました。
ここが重要です。
非同期処理を再利用したいときは、「呼び出し側でよく書いている同じパターン」を見つけて、それを Promise を返す関数にまとめる。
呼び出し側には“結果だけ”を渡せるようにすると、コードが一気にきれいになる。
「小さな非同期関数」を組み合わせて再利用性を上げる
1 個で全部やる関数は再利用しにくい
例えば、こういう関数があります。
function loadUserAndPosts() {
return fetch("/api/user")
.then((res) => res.json())
.then((user) => {
return fetch(`/api/posts?userId=${user.id}`)
.then((res) => res.json())
.then((posts) => {
return { user, posts };
});
});
}
JavaScriptこれ自体は便利ですが、
「ユーザーだけ欲しい」場面
「投稿だけ欲しい」場面
では再利用しづらいです。
「小さな非同期部品」に分けておく
まずは、粒度の小さい非同期関数を作ります。
function fetchUser() {
return fetchJson("/api/user");
}
function fetchPostsByUser(userId) {
return fetchJson(`/api/posts?userId=${userId}`);
}
JavaScriptこれらを組み合わせて、
「ユーザー+投稿」関数を作ればよいです。
function loadUserAndPosts() {
return fetchUser().then((user) => {
return fetchPostsByUser(user.id).then((posts) => {
return { user, posts };
});
});
}
JavaScriptこうすると、
ユーザーだけ必要 → fetchUser()
投稿だけ必要 → fetchPostsByUser(id)
両方必要 → loadUserAndPosts()
というふうに、
状況に応じて「部品」か「組み合わせ」かを選べる ようになります。
ここが重要です。
再利用性を上げるには、
「でかい非同期関数」 1 個で済ませるのではなく、
小さな Promise 関数をいくつか作って、それを組み合わせる構造にすること。
小さい部品ほど再利用しやすい。
「共通処理」をラップして再利用する
タイムアウト・リトライなどを「共通化」する
たとえば、いろんな API 呼び出しにタイムアウトをつけたいとします。
毎回こう書くのはツラいです。
function fetchWithTimeout(url, ms) {
return new Promise((resolve, reject) => {
const id = setTimeout(() => reject(new Error("タイムアウト")), ms);
fetch(url)
.then((res) => {
clearTimeout(id);
resolve(res);
})
.catch((err) => {
clearTimeout(id);
reject(err);
});
});
}
JavaScriptこれを「再利用可能なラップ関数」として定義しておけば、
どの API にも簡単につけられます。
function withTimeout(promiseFactory, ms) {
return (...args) =>
new Promise((resolve, reject) => {
const id = setTimeout(() => reject(new Error("タイムアウト")), ms);
promiseFactory(...args)
.then((res) => {
clearTimeout(id);
resolve(res);
})
.catch((err) => {
clearTimeout(id);
reject(err);
});
});
}
JavaScript既存の関数を「タイムアウト付き」にする:
const fetchJsonWithTimeout = withTimeout(fetchJson, 3000);
fetchJsonWithTimeout("/api/user")
.then((user) => console.log(user))
.catch((err) => console.error(err));
JavaScriptここでのポイントは、
withTimeout 自体が「非同期関数を再利用するための“メタ関数”」になっている
元の fetchJson を一切触らず、「タイムアウト付き版」を量産できる
ということです。
ここが重要です。
再利用のレベルが上がってくると、「非同期関数そのもの」だけでなく、「非同期関数に共通機能を足すラッパー」も再利用部品になる。
これによって、「どんな非同期処理にも同じルールを簡単に適用できる」ようになる。
「戻り値の形」を揃えるともっと再利用しやすくなる
成功 / 失敗を「例外」ではなく「値」で返す
ときどき便利なのが、
「失敗しても throw せず、“結果オブジェクト”として返す」スタイル の再利用関数です。
例えば、こういう関数を作ります。
function safeFetchJson(url) {
return fetchJson(url)
.then((data) => {
return { ok: true, data };
})
.catch((error) => {
return { ok: false, error };
});
}
JavaScript使う側:
safeFetchJson("/api/user").then((result) => {
if (result.ok) {
console.log("成功:", result.data);
} else {
console.error("失敗:", result.error);
}
});
JavaScriptこのパターンの良いところは、
catch を多用せず、「成功 / 失敗をひとまとめに扱える」
複数の API を Promise.all で並列実行しても、全部の結果を「配列の中身」として扱える
など、「扱う側の条件分岐がシンプルになる」 ところです。
ここが重要です。
非同期関数の再利用性を上げるためには、「返す形(成功 / 失敗の表現)」をある程度パターン化しておくと、呼び出し側のコードが揃って読みやすくなる。
初心者向け「非同期関数の再利用」の押さえどころ
最後に、本当に大事なポイントだけを整理します。
非同期処理を「その場の Promise」で書き捨てず、必ず「Promise を返す関数」に切り出す。
引数で動作を変えられるようにして、「同じ関数をいろんな場面から呼べる」形にする。
小さい非同期関数(fetchUser, fetchPosts など)を複数作り、それを組み合わせて大きな処理を作ると再利用性が上がる。
共通ロジック(fetch + エラーチェック、タイムアウト、リトライなど)は「ラッパー関数」として 1 箇所にまとめる。
返り値の形(成功 / 失敗の表現)をある程度揃えると、呼び出し側のコードがスッキリし、再利用しやすくなる。
ここが重要です。
非同期処理を「1 回きりのイベント」ではなく、「何度も呼べる部品」にする意識を持つこと。
そのために、“Promise を返す関数として切り出す → 小さく分解して組み合わせる → 共通処理はラップして共有する” という流れを習慣にすると、一気にコードの質が変わります。
練習としては、
いま自分が書いた非同期コードの中から、「同じような fetch + then + catch の塊」を1つ見つける
それを「Promise を返す関数」に切り出し、2 箇所以上から呼ぶ形に書き換えてみる
これを 1 回やってみると、
「あ、こうすれば何度でも同じ非同期処理をきれいに使い回せるんだ」
という感覚が、かなりハッキリ掴めてくるはずです。
