JavaScript フロントエンドのタイムアウト非同期操作に最適なソリューション

JavaScript フロントエンドのタイムアウト非同期操作に最適なソリューション

ECMAScript の Promise ES2015 と async/await ES2017 機能がリリースされて以来、非同期はフロントエンドの世界で特に一般的な操作になりました。非同期コードと同期コードが問題を処理する順序には若干の違いがあります。非同期コードを書くには、同期コードを書く場合とは異なる「意識」が必要です。

コードの実行に長い時間がかかる場合はどうなりますか?

これが同期コードの場合、「無応答」、または平易な言葉で言えば「死」と呼ばれる現象が発生しますが、非同期コードの場合はどうなるでしょうか。結果は得られないかもしれませんが、他のコードは何も起こらなかったかのように続行されます。

もちろん、実際に何も起こらなかったわけではなく、状況が異なれば異なる現象が起こるというだけです。たとえば、読み込みアニメーションのあるページは常に読み込み中のように見えます。別の例としては、データが更新されるはずなのにデータの変更が見えないページがあります。

例えば、どうやってもダイアログボックスを閉じることができない…このような現象をバグと呼びます。しかし、非同期操作プロセスが「エコー」せず、そこで静かに終了する場合もあります。誰もそれを知ることはなく、ページが更新された後は痕跡さえ残りません。

Axiosにはタイムアウト処理機能が搭載されている

Axios を使用して Web API 呼び出しを行うことは、一般的な非同期操作プロセスです。通常、コードは次のように記述されます。

試す {
    const res = axios.get(url, options); を待機します。
    //TODO 後続の業務を通常どおり続行します} catch(err) {
    // TODO フォールトトレランスを実行するかエラーを報告する}

このコードは通常はうまく機能しますが、ある日ユーザーから「なぜこんなに長く待っても応答がないのか」という苦情が寄せられます。

その後、開発者は、サーバーへの負荷が増大したため、この要求に即座に応答することが困難であることに気付きました。ユーザーの気持ちを考慮して、読み込みアニメーションが追加されます。

試す {
    読み込み中を表示します。
    const res = axios.get(url, options); を待機します。
    //TODO 通常業務} catch (err) {
    //TODO フォールトトレランス処理} finally {
    読み込みを非表示にします();
}

しかし、ある日、あるユーザーが「30 分待ったが、ぐるぐる回り続けるだけだ!」と言いました。そこで開発者は、何らかの理由でリクエストが停止していることに気付きました。この場合、リクエストを再送信するか、ユーザーに直接報告する必要があります。つまり、タイムアウト チェックを追加する必要があります。

幸いなことに、Axios はタイムアウトを処理できます。 optionstimeout: 3000を追加するだけで、この問題を解決できます。タイムアウトが発生した場合は、 catchブロックでそれを検出して処理できます。

試す {...}
キャッチ(エラー){
    if (err.isAxiosError && !err.response && err.request
        && err.message.startsWith("タイムアウト")) {
        // Axios リクエストが間違っていて、メッセージが遅延メッセージである場合 // TODO はタイムアウトを処理します}
}
ついに {...}

Axios は問題ありませんが、 fetch()を使用するとどうなるでしょうか?

fetch() タイムアウトの処理

fetch()自体にはタイムアウトを処理する機能がないため、タイムアウトを決定し、 AbortControllerを使用して「キャンセル」要求操作をトリガーする必要があります。

fetch()操作を中止する必要がある場合は、 AbortControllerオブジェクトからsignalを取得し、シグナル オブジェクトをfetch()オプションとして渡すだけです。おそらく次のようになります:

const ac = 新しい AbortController();
const {シグナル} = ac;
fetch(url, { signal }).then(res => {
    //TODO ビジネスを処理する});
 
// 1 秒後にフェッチ操作をキャンセルします setTimeout(() => ac.abort(), 1000);

ac.abort()signalにシグナルを送信し、そのabortイベントをトリガーし、その.abortedプロパティをtrueに設定します。 fetch()内部処理では、この情報を使用して要求を中止します。

上記の例はfetch()操作のタイムアウト処理を実装する方法を示しています。これを処理するためにawaitを使用する場合は、 fetch(...) setTimeout(...)を配置する必要があります。

const ac = 新しい AbortController();
const {シグナル} = ac;
タイムアウトを設定します(() => ac.abort(), 1000);
const res = await fetch(url, { signal }).catch(() => undefined);

リクエストの失敗を処理するためにtry ... catch ...を使用することを避けるために、エラーを無視するためにfetch()の後に.catch(...)が追加されます。エラーが発生した場合、 resundefined値が割り当てられます。実際のビジネス処理では、 resに識別可能なエラー情報を含めることができるように、より合理的なcatch()処理が必要になる場合があります。

ここで終了することもできましたが、各fetch()呼び出しごとにこのような長いコードを書くのは面倒なので、次のようにカプセル化しましょう。

非同期関数 fetchWithTimeout(timeout, resoure, init = {}) {
    const ac = 新しい AbortController();
    定数シグナル = ac.signal;
    setTimeout(() => ac.abort(), タイムアウト);
    return fetch(リソース、{...init、signal});
}

それでいいですか?いいえ、問題があります。

上記のコードのsetTimeout(...)でメッセージを出力すると、次のようになります。

タイムアウトを設定する(() => {
    console.log("タイムアウトです");
    ac.abort();
}、 タイムアウト);

そして、電話には十分な時間をかけましょう。

fetchWithTimeout(5000, url).then(res => console.log("success"));

出力successが表示され、5 秒後に出力It's timeout表示されます。

ちなみに、 fetch(...)のタイムアウトは処理しましたが、 fetch(...)が成功したときにtimer強制終了しませんでした。思慮深いプログラマーがどうしてこのような間違いを犯すのでしょうか?彼を殺せ!

非同期関数 fetchWithTimeout(timeout, resoure, init = {}) {
    const ac = 新しい AbortController();
    定数シグナル = ac.signal;    
    定数タイマー = setTimeout(() => {
        console.log("タイムアウトです");
        ac.abort() を返します。
    }、 タイムアウト);    
    試す {
        return await fetch(resoure, { ...init, signal });
    ついに
        タイマーをクリアします。
    }
}

完璧!しかし、問題はまだ終わっていません。

すべてがタイムアウトになる可能性がある

Axios と fetch はどちらも非同期操作を中断する方法を提供していますが、 abort機能を持たない通常の Promise の場合はどうなるでしょうか?

そのような約束に対して、私が言えるのは、彼を行かせなさい、彼が永遠にそれを続けさせなさいということだけです。どうせ私は彼を止めることはできません。でも人生は続いていくので、待ち続けることはできません!

この場合、 setTimeout()を Promise にカプセル化し、 Promise.race()を使用して「時間切れ後に待機しない」を実装できます。

Race はレースを意味するので、 Promise.race()の動作は理解しやすいですよね?

関数 waitWithTimeout(promise, timeout, timeoutMessage = "timeout") {
    タイマーを設定します。
    const timeoutPromise = 新しい Promise((_, 拒否) => {
        タイマー = setTimeout(() => 拒否(timeoutMessage)、タイムアウト);
    }); 
    Promise.race([timeoutPromise, promise]) を返します。
        .finally(() => clearTimeout(timer)); // タイマーをクリアするのを忘れないでください
}

効果をシミュレートするために Timeout を記述できます。

(非同期() => {
    const business = new Promise(resolve => setTimeout(resolve, 1000 * 10));
    試す {
        waitWithTimeout(ビジネス、1000) を待機します。
        console.log("[成功]");
    } キャッチ (エラー) {
        console.log("[エラー]", err); // [エラー] タイムアウト
    }
})();

上記は、JavaScript フロントエンドのタイムアウト非同期操作に対する完璧なソリューションの詳細な内容です。フロントエンドのタイムアウト非同期操作の解決の詳細については、123WORDPRESS.COM の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • JavaScript 非同期操作の一般的な処理方法の概要
  • js の非同期読み込みのための 3 つのソリューション
  • JavaScriptの非同期エラーを適切に処理する方法について簡単に説明します。
  • 非同期プログラミングの例外を解決するためにJavaScriptを学んでください
  • Vue axios リクエストのタイムアウトの正しい処理

<<:  MySQL での utf8mb4 照合の例

>>:  CSS で中空マスク レイヤーを実装するサンプル コード

推薦する

ES6 クラス継承を使用してゴージャスなボール効果を実現する方法

目次導入実装手順キャンバス環境を作成するライティングボールBallクラスを継承するMoveBallク...

Macにmysql5.7.18をインストールする詳細な手順

1. ツール今必要なツールは2つあります: MySQLサーバー (mysql-5.7.18)、MyS...

HTML+SassはHambergurMenu(ハンバーガーメニュー)を実装します

先日、外国人の方がHTML+CSSを使ってHamburgerMenuを実装している動画を見ました。最...

Ubuntu 18.0.4 は mysql をインストールし、エラー 1698 (28000): ユーザー ''root''@''localhost'' のアクセスが拒否されましたを解決します

序文最近 Linux を学び、その後 Win から Ubuntu に変更しました。以前インストールし...

CSSは、閉じることができるマスクレイヤーを備えたポップアップウィンドウ効果を実装します。

実際の開発ではポップアップウィンドウがよく使われます。CSS3を勉強していたときに、閉じることができ...

MySQL の 4 つのトランザクション分離レベルを例を使って分析する

序文データベース操作では、同時データ読み取りの正確性を効果的に保証するために、トランザクション分離レ...

ツリー チャートの実装方法に関する Echarts チュートリアル

ツリーマップは主にツリーのようなデータ構造を視覚化するために使用され、特殊なタイプの階層です。これを...

JS配列メソッドsome、every、findの使用に関する詳細

目次1. いくつか2. すべての3. 見つける1. いくつかsome()メソッドは、指定された関数の...

JDBCデータベースリンクと関連メソッドのカプセル化の詳細な説明

JDBCデータベースリンクと関連メソッドのカプセル化の詳細な説明MySQL データベースを使用して、...

HTML 画像 img にハイパーリンクを追加した後の醜い青い境界線の問題を解決する

HTML画像にハイパーリンクを追加すると醜い青い枠線が表示される次のように:解決: CSS スタイル...

UTF-8 ファイルの Unicode 署名 BOM (バイト オーダー マーク) の問題

最近、UTF8 エンコードの中国語 Zen Cart Web サイトをデバッグしているときに奇妙な現...

Vue大画面表示適応方法

この記事では、vueの大画面表示適応の具体的なコードを参考までに紹介します。具体的な内容は以下のとお...

MySQLスケーラブル設計の基本原則

目次序文1. スケーラビリティとは何ですか?スケールアウトの利点:スケールアウトのデメリット:スケー...

CentOS 7.4 64 ビット版に MySQL 8.0 をインストールして設定するための詳細な手順

ステップ1: MySQL YUMソースを取得するMySQLの公式サイトにアクセスして、RPMパッケー...

MySQL 8.0.13 zipパッケージのインストール方法について

MySQL 8.0.13 にはデフォルトでデータ フォルダがあります。このフォルダを削除する必要があ...