前回の記事でコールバック関数の登場とその課題について整理しました。
今回はそんなコールバック関数の問題を解決したPromiseについて記載しています。
今回もSoftware Design 2023年9月号の内容をまとめています。
▼参考文献
Promiseを用いた非同期処理の改善
非同期処理では処理結果の値をすぐには使えないという性質がある。
同期処理のように結果を関数の戻り値にできない。例えば次のような投稿取得処理getPostが処理結果となる結果postを返すことはできない。
// 非同期処理では結果をすぐに返せない
const post = getPost(postId);
結果を次の処理に受け渡すために、どうしてもコールバック関数の受け渡しが必要でした。この制約がコールバック地獄に繋がっていた。
この問題を解決するために、投稿取得処理getPostは将来得られるであろう投稿postをPromiseオブジェクトpostPromiseとして返すことができる。
// 結果の代わりにPromiseを返す
const psotPromise = getPost(postId);
Promiseが返された時点では結果が確定していない場合もあるため、PromiseからPromise.resultのように結果を参照することはできない。
その代わり、Promiseのthenメソッドにコールバック関数を渡すことで、結果が確定した時にその値を参照できる。
以下では前回の記事で書かれたコールバック関数によって生まれた2つの問題をPromiseがどのように解消したか記述しています。
コールバック地獄を解消
Promiseの特徴は、同期処理の関数呼び出しと戻り値の関係と同じ形で書けるようになるところ。そのためコールバック地獄の例で見たような処理の入れ子が発生しなくなる。
コールバック地獄
getPost(postId, (post) => {
getUser(post.userId, (user) => {
getOrganization(user.orgId, (org) => {
// さらにコードがネストされて読みにくくなる
});
});
});
非同期処理がフラットになった様子
const promise1 = getPost(postId);
const promise2 = promise1.then((post) => getUser(post.userId));
const promise3 = promise2.then((user) => getOrganization(user.orgId));
不統一なコールバックAPIを解消
コールバック関数APIには標準的なパターンがなく、各APIは独自のパターンを持っていた.そのため「開発者は各APIごとにコールバックパターンを正しく理解する」必要があり、実装の障壁となったり、コールバック地獄をさらに複雑化させたりする要因になっていた。
そこでPromiseが導入されたことで、上記のような問題も解消された。
不統一なコールバックAPI
// readFile関数(Node.jsのAPIで、ファイルを読み込む関数)
import { readFile } from "fs";
readFile("file.txt", "utf8", (error, data) => {
if (error) {
// 失敗時の処理
} else {
// 成功時の処理
}
});
/*================================================*/
// XMLHttpRequest(HTTPリクエストを送信するAPI)
onst xhr = new XMLHttpRequest();
xhr.open("GET", "http://api.example.com");
xhr.onload = () => {
// 成功時の処理
};
xhr.onerror = () => {
// 失敗時の処理
};
xhr.send();
Promiseの導入
// Promise化されたreadFile
import { readFile } from "fs/promises";
readFile("file.txt", "utf8")
.then((data) => {
// 成功時の処理
})
.catch((error) => {
// 失敗時の処理
}
});
// Promise化したXMLHttpRequest(Promiseを返すfetchを用いる方法)
fetch("http://api.example.com")
.then((response) => {
// 成功時の処理
})
.catch((error) => {
// 失敗時の処理
}
非同期処理にPromiseが用いられると、全く異なる目的や機能を持つAPIでも、同じ形式で書くことができるようになった。
こうした非同期処理の統一性が、JavaScriptのPromiseがもたらした最も重要な改善の一つ。