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 で中空マスク レイヤーを実装するサンプル コード

推薦する

Dockerは指定されたメモリで操作を実行します

次のように: -m, --memory メモリ制限。形式は数値と単位です。単位は b、k、m、g の...

MySQLで自動作成時間と変更時間を設定する方法の例

この記事では、MySQL の自動作成時刻と変更時刻を設定する方法について説明します。ご参考までに、詳...

MySQL データベース データのロード 複数の用途

目次MySQL Load Dataの多様な用途1. LOAD の基本的な背景2. 基本パラメータをロ...

grpc のリバース プロキシとして nginx を使用する場合の落とし穴の概要

背景ご存知のとおり、nginx は高性能な Web サーバーであり、負荷分散やリバース プロキシによ...

MySQLの複合インデックス方式の詳細な説明

どの DBMS でも、インデックスは最適化にとって最も重要な要素です。データ量が少ない場合、適切なイ...

MySQLクエリ条件のnot inとinの違いと理由

まずSQLを書く SELECT DISTINCT from_id タラから cod.from_id ...

MySQLデータベースのnullに関する知識ポイントのまとめ

MySQL データベースでは、null は一般的な状況です。MySQL での null に関する注意...

Linux でメモリ使用量を確認する方法

システムの問題、アプリケーションの速度低下、または原因不明の問題をトラブルシューティングする場合、最...

Windows 10 での MySQL 8.0.11 圧縮バージョンのインストール チュートリアル

この記事では、MySQL 8.0.11圧縮版のインストールチュートリアルを参考までに紹介します。具体...

異なる列を持つテーブルのクエリ結果のSQLマージ操作

2 つの異なるテーブルをクエリするには、結果をマージする必要があります。たとえば、table1 の列...

CentOS7 (YUM) での MySQL 5.7 のインストールと設定のチュートリアル

インストール環境: CentOS7 64ビット、MySQL5.7 1. YUMソースを設定するMyS...

HTMLフォーム要素の詳しい解説(パート1)

HTML フォームは、さまざまな種類のユーザー入力を収集するために使用されます。 HTML フォー...

CSS が最初のサイクルで画像を読み込むために @keyframes を使用するときに発生するホワイトギャップの問題 (フラッシュ画面) をすばやく解決します。

問題の説明: CSS アニメーション プロパティを使用すると、ループが最初に読み込まれたときに白いギ...

Win7 で IIS7 Web および FTP サービスを完全にアンインストールする方法

昨日、パソコンにPHP開発環境をセットアップした後、Apacheサーバーを再起動するとエラーが続きま...

select count() と select count(1) の違いと実行方法

Count(*) または Count(1) または Count([column]) は、おそらく S...