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

推薦する

CSS でのフィルタープロパティの使用に関する詳細な説明

フィルター属性は要素の視覚効果を定義しますぼかし画像にガウスぼかしを適用します。 「半径」の値は、ガ...

VMware Workstation Pro が Win10 アップデートにより開けなくなる問題の解決方法

今夜の夕食後にノートパソコンの電源を入れたところ、問題が発生しました。通常、コンピューターがスリープ...

MySQLでconcat関数を使用する方法

以下のように表示されます。 //managefee_managefee テーブルの年と月を照会し、c...

Docker データ管理 (データ ボリュームとデータ ボリューム コンテナー) の詳細な説明

実稼働環境で Docker を使用する場合、多くの場合、データを複数のコンテナ間で永続化または共有す...

MySQL EXPLAIN ステートメントの使用例

目次1. 使用方法2. 出力結果1.id 2.選択タイプ3.表4.パーティション5.タイプ6.可能な...

Linux システムの /etc/fstab ファイルの詳細な解釈

序文 [root@localhost ~]# cat /etc/fstab # #/etc/fsta...

モバイルデバイスにおける適応レイアウトの問題に関する簡単な説明 (レスポンシブ、rem/em、Js ダイナミクス)

3G の普及により、携帯電話を使ってインターネットにアクセスする人が増えています。モバイル デバイ...

Nginx プロキシ使用時にヘッダーに「_」が含まれることで情報が失われる問題の解決方法

序文ゲートウェイプロジェクトを開発する場合、署名 sign_key 情報はリクエスト時にリクエスト ...

CSS3アニメーションとHTML5の新機能の詳しい説明

1. CSS3アニメーション☺CSS3 アニメーションは、JavaScript を介して要素のスタイ...

MySQL マスタースレーブ同期の原理と応用

目次1. マスタースレーブ同期原理マスタースレーブ同期アーキテクチャ図(非同期同期)マスタースレーブ...

MYSQL での Truncate の使用法の詳細な説明

この記事のガイド: テーブル内のデータを削除するには、削除と切り捨ての 2 つの方法があります。TR...

Linux でタスク用のカスタム システム トレイ インジケーターを作成する

システム トレイ アイコンは、今日でも魔法のような機能です。アイコンを右クリックして目的のアクション...

Linux で静的ルーティングを追加するための 2 つの実装方法の分析

ルートを追加するコマンド: 1.ルート追加route add -net 192.56.76.0 ne...

Mysql データベースの日付と日時型でデフォルト値 0000-00-00 を設定するときに発生するエラー問題の詳細な説明

現象: MySQL バージョン 5.7 以降では、日付型と日付時刻型のデフォルト値が「0000-00...