現代の Web 開発は、応答性の高い対話型アプリケーションを実現するために、非同期アクティビティに大きく依存しています。 API からのデータの取得、ファイルの読み取り、タイマーの実行のいずれであっても、これらのプロセスはインターフェイスをフリーズさせることなくバックグラウンドで実行する必要があります。 JavaScript は、これらのジョブを処理するための信頼できる方法を提供します。この記事では、エラーのない非同期プログラムを開発するために、基本的な考え方や高度な機能など、Promise について知っておくべきことをすべて説明します。
この記事では、
について学びます。約束とは何ですか?
Promise を使用する理由
約束の仕組み
約束の処理
約束の連鎖
Promise でのエラー処理
高度な Promise 機能
Promise を使用した JavaScript の実行フロー (重要)
Promise チェーンを非同期/待機に変換する
ベストプラクティスとよくある間違い
JavaScript における Promise は、将来何かを行うという「約束」をすることと同じです。約束するということは、「後で結果を与えると約束します」と言っていることになります。この結果は成功か失敗になる可能性があります。
言い換えると、Promise は、非同期操作の最終的な成功 (または失敗) とその結果の値を反映するオブジェクトです。これにより、ハンドラーと非同期アクションの成功または失敗を関連付けることができるため、コードが読みやすく、保守しやすくなります。
たとえば JavaScript では、サーバーからのデータの取得など、時間のかかる操作は通常、コールバックで実行されていました。コールバックは、タスクの完了後に実行するために別の関数に渡される単なる関数です。たとえば、サーバーからデータが到着したときにデータを処理するためにコールバックを使用できます。
しかし、複雑な操作がある場合、コールバックの使用はかなり面倒になります。この混乱は「コールバック地獄」として知られており、あるコールバックが別のコールバックの中に存在する可能性があり、これによりコードが読み取れなくなり、管理できなくなります。
コールバック地獄の例:
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
上で示したように、このようなコードは、深く入れ子になった構造により、大規模なコードベースでは読み取りと保守がますます困難になり、「コールバック地獄」と呼ばれることもあります。
この問題に対処するために Promise が導入され、より読みやすい方法でチェーンできるようにすることで、非同期タスクを処理するためのよりクリーンで組織化された方法が提供されました。
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
このアプローチにより構造が平坦化され、コードがより読みやすく保守しやすくなります。
JavaScript の Promise は、次の 3 つの状態のいずれかになります。
保留中: これは最初のステップです。その約束はまだ果たされていません。
履行: Promise は正常に完了しました。これは、解決されて値があることを意味します。
拒否されました: Promise は正常に完了せず、エラー メッセージが表示されます。
基本構文
fetchData() .then(processData) .then(saveData) .then(console.log) .catch(console.error);
この例では、Promise は 1 秒後に「Promisesolved!」というメッセージとともに解決されます。 .then() メソッドは、解決された値を処理するために使用されます。
.then() メソッドは、Promise が正常に完了したときに何が起こるかを処理するために使用されます。 Promise が実行されたときに実行する関数 (コールバック) を登録します。
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved!"); }, 1000); }); myPromise.then(result => console.log(result));
.catch() メソッドは、Promise が失敗した場合に何が起こるかを処理するために使用されます。 Promise が拒否されたときに実行する関数 (コールバック) を登録します。
myPromise.then(data => { console.log("Data received:", data); });
.finally() メソッドを使用すると、成功したかどうかに関係なく、Promise の完了後にコードを実行できます。
myPromise.catch(error => { console.error("Error:", error); });
チェーンを使用すると、前のタスクの結果を渡すことでタスクを順番に実行できます。次に、next.then() に進みます。これにより、複数の非同期タスクを順番に処理できるようになります。
チェーンの例:
myPromise.finally(() => { console.log("Cleanup tasks"); });
この例では、プロセスの各ステップを処理するために each.then() を使用し、明確なデータ フローを可能にします。これにより、あるステージの結果が次のステージにどのように転送されるかを確認できます。
Promise では、解決のためにチェーンを .catch() メソッドに渡すことができるため、エラー処理が簡素化されます。これにより、各フェーズで障害を処理する必要がなくなり、コードがより明確になり、管理が容易になります。
エラー伝播の例:
fetch('https://api.example.com/user') .then(response => response.json()) .then(data => { console.log("Processed data:", data); return processData(data); }) .then(finalResult => { console.log("Final result:", finalResult); }) .catch(error => console.error("Error:", error));
Promise チェーンのいずれかのステップが失敗した場合、エラーは .catch() ブロックによって捕捉されます。これにより、問題の処理が容易になり、コードをスムーズに実行し続けることができます。
Promise.all() メソッドを使用すると、複数の Promise を同時に実行し、それらがすべて完了するのを待つことができます。すべての約束が果たされると、それぞれの結果が得られます。 Promise が失敗すると、間違いが検出されます。
fetchData() .then(processData) .then(saveData) .catch(error => console.error("An error occurred:", error));
この例では、いずれかの Promise が失敗すると、Promise.all() 全体が失敗します。
Promise.race() メソッドは、成功したか失敗したかに関係なく、終了した最初の Promise の結果を返します。
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
この例では、最初に完了した Promise (fetchData1 または fetchData2) の結果がコンソールに記録されます。
Promise.allSettled() メソッドは、指定されたすべての Promise が成功または失敗の状態になるまで待機してから終了します。その後、各 Promise の結果を含む配列が返されます。
fetchData() .then(processData) .then(saveData) .then(console.log) .catch(console.error);
この例では、Promise.allSettled() は fetchData1() と fetchData2() の両方が完了するのを待ちます。次に、各 Promise のステータスと結果 (またはエラー) をログに記録します。こうすることで、成功したか失敗したかに関係なく、各約束で何が起こったかを確認できます。
Promise.any() メソッドは、Promise のリストから最初の Promise が正しく解決されるまで待機します。少なくとも 1 つの Promise が解決された場合、その値は Promise.any() メソッドによって返されます。すべての Promise が拒否された場合、このメソッドはエラーをスローします。
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved!"); }, 1000); }); myPromise.then(result => console.log(result));
この例では、Promise.any() は最初の Promise が正常に解決されるまで待機します。このプロシージャは、最初に成功した Promise の結果を返します。この場合は、Promise2 の値が「Success A」です。すべての Promise が拒否された場合は、.catch() ブロックが実行され、エラー メッセージが記録されます。この戦略は、残りを待たずに最初に成功した Promise の結果を受け取りたい場合に有益です。
これを説明する例を次に示します:
myPromise.then(data => { console.log("Data received:", data); });
この例では:
console.log(2) は通常の同期操作であるため、最初に実行されます。
console.log (6) も同期なので次に実行されます。
Promise は優先度の高いマイクロタスクであるため、setTimeout コールバックの前に Promise の.then() が実行され、3 が出力されます。
最後に、setTimeout コールバックが実行され、マクロタスクとして 4 が出力されます。
マイクロタスクキューの優先順位により、promise の.then() は setTimeout コールバックの前に実行されることを常に覚えておいてください。
JavaScript では、コードは特定の順序で実行されます。最初に同期コード、次にマイクロタスク (Promise など)、最後にマクロタスク (setTimeout など) です。
これを説明する一例を次に示します:
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
この例では、同期コードが最初に実行され、3、6、2、7、8 が記録されます。同期コードが終了すると、マイクロタスク (.then() コールバック) が処理され、1 と 9 が記録されます。最後に、マクロタスク(setTimeout から) 遅延の順に実行され、21 (0ms) と 13 (10ms) が記録されます。これは、JavaScript の実行順序を強調しています: 同期コード > ;マイクロタスク >マクロタスク。
Promise を作成する場合、解決または拒否するための最初の呼び出しのみが重要です。他のすべての通話は拒否されます。
これを説明する例を次に示します:
fetchData() .then(processData) .then(saveData) .then(console.log) .catch(console.error);
この例では、Promise は値 1 で解決されます。Promise は最初の解決ですでに解決されているため、2 番目の解決と拒否呼び出しは無視されます。
Promise をチェーンすると、each.then() がプロセスのステップを処理します。
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved!"); }, 1000); }); myPromise.then(result => console.log(result));
この例では、Promise.resolve(1) は値 1 で始まりますが、最初の .then(() => 2) は代わりに 2 を返します。次の .then(3) は無視され、値 2 が渡されます。 .then((value) => value * 3) は値を 3 で乗算し、結果は 6 になります。.then(Promise.resolve(4)) は値を変更せず、最後に .then(console. log) logs 6. これは、関数以外の値が無視され、値がどのようにチェーンを通過するかを示しています。
myPromise.then(data => { console.log("Data received:", data); });
この例では、Promise 解決のさまざまな段階がどのように処理されるかを示すために、複数の.then()、.catch()、および.finally() メソッドを連鎖させています。細かく見てみましょう:
finally() は引数を受け取りません:
finally() ブロックはクリーンアップ コードを実行しますが、値を受け取ったり渡したりしません。これは、Promise の結果に関係なく、特定のコードが確実に実行されるようにするために使用されます。
finally() で値を返しても、Promise には影響しません:
finally() ブロックで値を返した場合、Promise チェーンや最終値には影響しません。 Promise の解決/拒否の後に実行されますが、結果は変更されません。
finally() でエラーをスローすると拒否が発生します:
エラーをスローするか、finally() で拒否された Promise を返すと、Promise チェーンがエラーまたは拒否の理由で拒否されます。
myPromise.catch(error => { console.error("Error:", error); });
または
myPromise.finally(() => { console.log("Cleanup tasks"); });
例:
fetchData((data) => { processData(data, (processedData) => { saveData(processedData, (result) => { console.log(result); }); }); });
Async/await は、コードを同期モードで記述されたコードに近づける Promise を使用するメソッドです。よく使用される用語は「構文シュガー」です。これは、非同期コードを実行するためのより簡単でクリーンなパスを提供するためです。
fetchData() .then(processData) .then(saveData) .then(console.log) .catch(console.error);
Promise.all() を使用して、Promise と async/await を組み合わせて並列実行できます。
const myPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Promise resolved!"); }, 1000); }); myPromise.then(result => console.log(result));
深いネストを避ける: コードをフラットで読みやすい状態に保つには、チェーンまたは async/await を使用します。
常にエラーを処理する: すべての Promise チェーンに .catch() または try/catch ブロックがあることを確認してください。
並列実行を賢く使用する: タスクは独立しているが、まとめて終了する必要がある場合にのみ Promise.all() を使用します。
JavaScript Promise は、サーバー上のデータの取得など、時間のかかる操作に対処する最良の方法の 1 つです。これらは、よりクリーンで保守が容易なコードを作成するのにも役立ち、学んだ内容を実践することで、非同期コーディングを最大限に活用できるようになります。実践的な経験を積み、エラーをエレガントに処理できるようになると、Promise は JavaScript の非常に重要な部分を占めるようになります。
読んでいただきありがとうございます!この記事が役に立ったと思われる場合は、お気軽にハイライト、拍手、コメントを残すか、Twitter/X や LinkedIn で私までご連絡ください。このようなコンテンツを無料で提供できることは非常にありがたいことです。
以上がJavaScript Promise とその仕組みについて知っておくべきことすべての詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。