シーンの説明
最近の開発では、次のようなシナリオに遭遇しました。
- ブロックのレンダリングには複数のインターフェイスを取り込む必要があります。スプラッシュ スクリーンを削除して優れたユーザー エクスペリエンスを確保するにはどうすればよいでしょうか?
- インターフェース内のデータ量が膨大で、処理ロジックが肥大化し、コードがわかりにくく、その後のメンテナンスが困難な場合はどうすればよいでしょうか?
- ビジネス ロジックは特殊ですが、プロジェクト内のインターフェイスに制御可能なタイムアウト再試行メカニズムをエレガントに設定するにはどうすればよいでしょうか?
一見すると、それらはすべてビジネスで遭遇するさまざまなアプリケーション シナリオに基づいています。ただし、実際の開発では、これらはすべて Promise 非同期プログラミングと切り離せない関係にあります。上記の問題を実際に解決した後、その解決策が二次拡張のための基本的な知識に実際に基づいていることを見つけるのは難しくありませんPromise
。
Promise
一般的に使用される静的メソッドについて理解を深め、今後同様の問題を解決する効率を向上させるために、この記事をまとめました。これが私やより多くの学生に知識の蓄積とインスピレーションを与えることができれば幸いです。
1. プロジェクトの適用シナリオ
シナリオ 1. 複数のインターフェイスを同時にプルする
私がこの問題に初めて遭遇したのは要件内で、次のような効果を達成する必要がありました。
-
「進行中」をクリックする
Tab
とLoading
ステータスが表示されます -
フロントエンドは「応答」と「テストを受けていない」という 2 つのテスト インターフェイスを同時にプルします。すべての結果が返されたら、「応答」データを先頭に置きます。
-
キャンセル
Loading
ステータス、表示リスト
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-aUc01YRM-1654666968156)(https://p1-juejin.bytaimg.com/tos) -cn-i-k3u1fbpfcp /a3178bd3a2e841d7839cc7c1aad42338~tplv-k3u1fbpfcp-zoom-in-crop-mark:1956:0:0:0.image?)]
コンポーネントのレンダリングに複数のインターフェイスからのデータが必要な場合、同期コードを使用してリクエストを送信すると、インターフェイスの戻り時間が一貫していないことが多いため、問題が発生します。多くの場合、一部のインターフェイスが最初に返された結果を取得してレンダリングし、その後、他のインターフェイスが返された結果を取得してビューを再度更新することで、ページ上に「スプラッシュ画面」が発生することがよくあります。この種の問題は、次の方法でうまく回避できます。を使用してPromise.all()
:
ただし、コールバック メソッドを使用する場合、コードがコールバック メソッドに入る前に、すべての受信状態が変化するか、いずれかの状態が変化するまでPromise.all()
待つ必要があることに注意してください。このときのページのレンダリング時間は、全インターフェースの戻り時間の最大値となります。Promise
Fulfilled
Promise
Rejected
Promise.all
拡張:別の値に関する考慮事項があります。受信Promise
状態が変化し、独自のメソッドがRejected
存在しない場合は、コールバックが入ります。これは、重要でないインターフェイスだけがハングアップすることがありますが、それを使用しているため、ページ全体をレンダリングできないことを意味します。各受信リクエストにメソッドを追加することで、複数のリクエストをマージできます。一部のリクエストが失敗した場合でも、それらのリクエストは返されます。データとエラー ロジックを 1 か所で処理するだけで済みます。catch
Promise.all
catch
Promise.all()
Promise
catch
上記の変更後、受信Promise
リクエスト インターフェイスが失敗した場合、カスタマイズされたエラー メッセージがRejected
最初に送信されます。そして、受信メソッドに対してPromise
独自のメソッドを定義しますcatch
。これは新しいPromise
インスタンスを返し、メソッドが実行された後catch
も になりますResolved
。この方法では、Promise.all()
メソッド パラメータに両方のインスタンスが含まれるため、メソッド内でデータとエラー ロジックを均一に処理Resolved
できます。then
上記の処理後、これを使用する場合Promise.all()
、リクエストの失敗によりページ全体がデータをレンダリングできなくなることを心配する必要がなくなります。
補足:実は上記のようなPromise.all()
運用上の問題を解決するために、ES2020ではPromise.allSettled()
非同期操作群が終了したかどうかを判定する方法が導入されました。と比較した場合Promise.all()
、その最大の利点は、パラメータ インスタンスに関係なくResolve
、メソッドの最初のコールバック関数がReject
パラメータインスタンスの状態に移行せずにPromise.allSettled()
実行されることです。then
catch
Rejected
同時に、axios
自分で返却する場合はPromise
追加の外装梱包は必要ないことを思い出していただいた学生の皆様に感謝しますPromise
。ただし、Promise.allSettled()
まだ TC39 フェーズ 4 ドラフトの段階にあるため、使用する際は互換性の問題に注意する必要があります。互換性比較表が添付されています。
Promise.allSettled()
(データはPromise.allSettled() - MDN 、2021-12-08から取得)
Promise.all()
(データはPromise.all() - MDN、2021-12-08 から取得)
シナリオ 2. 非常に大量のデータを含むインターフェイス コールバック処理
インターフェイスによってプルされたデータと関連するエラー ロジックをコールバックで同時に処理できるPromise.all()
ように、使用法を改善する方法を上で説明しました。Promise.all().then()
ただし、同時にプルする必要があるインターフェイスの数が増加するにつれて、Promise.all().then()
コールバックに記述する必要があるロジックがますます肥大化していくことがわかります。たとえ上記が 2 つの単純なインターフェイスのみを扱っていたとしても、そのif/else
ロジックはすでにかなりの読みづらさを引き起こしています。しかし、この種の問題は実際にはそれだけがPromise.all()
原因ではなく、インターフェースが別の処理(上記の 2 つ以上のインターフェースを 1 つにマージするなど)が必要な大量のデータを返す場合、どのように処理するかを考慮する必要もあります。コードの読みやすさを最適化するためにここで行います。
実際、柔軟な使用によりPromise.prototype.then()
さまざまなデータ処理ロジックを分離するミドルウェア機能を実装できます。
ミドルウェアとして機能することでPromise.prototype.then()
、コードをさまざまな論理処理ブロックに明確に分割できます。さまざまな論理処理を関数にカプセル化することもできます。こうすることで、関数全体のロジックが、あなたや後続の他のメンテナにとって読みやすくなります。
シナリオ 3. インターフェイスの再試行の実装
ユーザーに十分なエクスペリエンスを提供するために、プロジェクトの特定のシナリオでは、インターフェイスのタイムアウト再試行操作をいくつか実行します。たとえば、ユーザーは地下鉄でコースを閲覧しており、コースではユーザーの学習時間を記録する必要があります。ネットワークが一時的に利用できなくなった場合、ネットワークが一時的に利用できなくなり、ユーザーの学習が記録できないという状況を避けるために、ユーザーが気づかないうちに学習時間記録インターフェースを再リクエストする再試行メカニズムが必要です。
もちろん、このタイムアウト再試行メカニズムはAJAX
すべての主要なライブラリに実装する必要があります。ただし、fetch
ネイティブのものを使用する場合Web API
、インターフェースのリトライの実装を使いこなせないと少し面倒になります。実際、Promise.race()
を柔軟に使用することで、インターフェイスの再試行ロジックを簡単に実装できます。
上記のコードでは、timeout
関数とリクエストrequest
関数を定義しました。同時に、ajax
受信リクエストurl
、タイムアウト期間、および再試行回数に対する関数が定義されます。request
インターフェイスがタイムアウト期間内に戻らない場合は、Promise.race()
定義したtimeout
関数によってインターフェイスが削除されますRejected
。上記のシナリオ 1 とシナリオ 2 で説明した方法を通じて、同じ場所で受信Promise
定義関数の最終原因を特定し、リクエストの正常なリターン/タイムアウトに対応するロジックを実行できます。catch
Promise.race
Resolved
2. 応用シナリオの拡大
シナリオ 1. リクエストのバッチ処理
-
同時にロードできる画像の最大数は次のとおりです。
maxNum
-
イメージのロードが成功または失敗すると、アンロードされた残りのイメージをロードするためのスペースが確保されます。
-
すべての画像が読み込まれた後、結果が順番に印刷されます。
このシナリオはChris Jensenによるもので、彼が書いた興味深いPromise.race()
メソッドの使用例に基づいて、画像のバッチ読み込みをシミュレートしようとしました。
シナリオ 2. できるだけ早くリソースを入手する
逆にPromise.all()
、反復可能なオブジェクトPromise.any()
を受け取りPromise
、そのうちの 1 つが成功する限りPromise
、成功した Promise が返されます。したがって、この機能を使用して、最初に正常にロードされたイメージを取得できます。たとえば、画像をランダムに表示する必要がある一部のシーンでは、複数の画像を一度にロードし、それを使用してPromise.any()
最速の記録画像を取得し、最初の画面のレンダリング速度を向上させることができます。複数のサーバーがある場合は、Promise.any()
最も速い応答でサーバーから必要なリソースを取得できます。
知らせ!Promise.any() メソッドはまだ実験段階にあり、すべてのブラウザで完全にはサポートされていません。
3. まとめ
1. Promise.race()
:
- 多くの場合、オンデマンド キャンセルのシナリオで使用され、タイムアウトの中断/再試行の効果を実現するために
Promise
タイマー入力メソッドと組み合わせて使用されます。Promise.race()
promises
バッチ処理操作の場合、配列を維持し、タイムリーに最速のものを処理するメソッドを使用する必要race()
があります。待機キューに後続のタスクのためのスペースを確保するresolved
Promise
2. Promise.all()
- これは基本的に、レンダリング中に同時に複数のインターフェイス データ/フロントエンド集計データを必要とするシナリオで使用されます。オンデマンドで受信パラメータの関数
Promise
を定義するcatch
と、同じ場所でデータ レンダリング/インターフェイス エラー ロジックを処理しやすくなり、小さなインターフェイスが原因で問題が発生し、ページ全体をレンダリングできません。
3. Promise.prototype.then()
- 異なるコードロジックを分離するミドルウェアとして柔軟に使用できます
then()
。これにより、コールバック関数のビジネス ロジックが肥大化して保守が困難になる状況を回避できます。 then()
連鎖呼び出しは、インターフェイス間の相互依存性を単純化するのにも適していますが、async/await
ほとんどの場合、より優れていることは間違いありません。
4. Promise.any()
- まだ完全にはサポートされておらず、互換性も低いため、当面は「最速のサーバーからリソースを取得する」などのニッチなシナリオでのみ使用できます。