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

推薦する

JS は VUE コンポーネントに基づいて都市リスト効果を実装します

この記事の例では、VUEコンポーネントに基づいて都市リストエフェクトを実装するための具体的なコードを...

vue3.0共通コンポーネントの自動インポート方法の例

1. 前提条件インポートには require.context メソッドを使用します。vite で作成...

Node.jsはexpress-fileuploadミドルウェアを使用してファイルをアップロードします

目次プロジェクトを初期化するサーバーの作成クライアントを初期化するコンポーネントの記述ファイルアップ...

MySQL のロックの仕組みと使用法の分析

この記事では、例を使用して MySQL のロック メカニズムと使用方法を説明します。ご参考までに、詳...

win2008R2 64 ビット システムでの mysql5.7.17 のインストールと構成の例

123WORDPRESS.COM では、さまざまな環境での MYSQL の他のバージョンのインストー...

uni-appがNFC読み取り機能を実装

この記事では、参考までに、NFC読み取り機能を実装するためのuni-appの具体的なコードを紹介しま...

モバイルレイアウト用の動的REMの実装

ダイナミックレム1. まず、現在の長さの単位を紹介しましょうpx em Mの幅 / 漢字の幅 1em...

純粋な CSS3 で水平無限スクロールを実装するためのサンプル コード

この記事の例はすべて小さなプログラムで書かれていますが、実装される機能には影響しません。 wxmlル...

CentOS 7.2 は uniapp プロジェクトを展開するための nginx Web サーバーを構築します

Pantherは新人としてスタートし、今もまだ新人ですが、人々から学び、学んだことを時々皆さんと共有...

MySQL の全体的なアーキテクチャの紹介

MySQL の全体的なアーキテクチャは、サーバー層とストレージ エンジン層に分かれています。サーバー...

MySQL データベース開発仕様 [推奨]

最近、問題のある新しい SQL が本番データベースに入力される数を最小限に抑えるために、開発仕様を整...

Docker を使用して Go Web アプリケーションをデプロイする方法

目次なぜ Docker が必要なのでしょうか? Docker デプロイメントの例コードの準備Dock...

CSSスタイルは、テキストが長すぎる場合に省略記号を表示する問題を解決します

1. CSSスタイルは、テキストが長すぎる場合に省略記号を表示する問題を解決します1. 一般的なスタ...

CSSはグラデーションを巧みに利用して高度な背景光アニメーションを実現します

成し遂げるこの効果は CSS を使用して完全に再現することは困難です。 CSS でシミュレートされた...

VueのTodoListケースの詳しい説明

<テンプレート> <div id="ルート"> <...