JavaScript進捗管理の詳しい説明

JavaScript進捗管理の詳しい説明

序文

プログラムを作成するときに、読み込みの進行状況やアップロードの進行状況など、進行状況を表示する必要に迫られることがよくあります。
最も一般的な実装方法は、完了数量 (loadedCount) と合計数量 (totalCount) を記録し、進捗状況を計算することです。
この方法は単純かつ大雑把で、実装は簡単ですが、拡張が難しく、すべての loadedCount と totalCount を維持する場所が必要です。
この記事では、上記の実装方法をベースに、より簡単にスケーラブルな進捗管理方法を実装します。

質問

私は WebGL アプリケーションを作成しており、アプリケーションのプリロード フェーズ中にロードの進行状況を計算する必要があります。
読み込まれるコンテンツには、モデル リソース、マップ リソース、スクリプト リソースなどが含まれます。
モデル リソースにはマテリアル リソースが含まれ、マテリアル リソースにはテクスチャ リソースが含まれます。
これを図で表すと、構造は次のようになります。

+-------------------------------------------------------------+
| |
| リソース |
| |
| +----------+ +-----------------+ +-----------------+ |
| | スクリプト1 | | モデル1 | | モデル2 | |
| +----------+ | | | | |
| | -------------+ | | -------------+ | |
| +----------+ | |model1.json | | | |model2.json | | |
| | スクリプト2 | | +-------------+ | | +-------------+ | |
| +----------+ | | | | |
| | +-------------+ | | +-------------+ | |
| +----------+ | | 素材1 | | | | 素材1 | | |
| | テクスチャ1 | | | +--------+ | | | | +--------+ | | |
| +----------+ | | |テクスチャ1| | | | | |テクスチャ1| | | |
| | | +--------+ | | | | +--------+ | | |
| +----------+ | | +--------+ | | | | +--------+ | | |
| | テクスチャ2 | | | |テクスチャ2| | | | | |テクスチャ2| | | |
| +----------+ | | +--------+ | | | | +--------+ | | |
| | +-------------+ | | +-------------+ | |
| | | | | |
| | +-------------+ | | +-------------+ | |
| | | マテリアル2 | | | | マテリアル2 | | |
| | +-------------+ | | +-------------+ | |
| +-----------------+ +-----------------+ |
| |
+-------------------------------------------------------------+

ここで前提となるのは、リソースをロードするときに、ロードが完了したと見なされる前に、リソースとそれが参照するリソースがすべてロードされていることを確認する必要があるということです。
この前提に基づいて、サブリソースの読み込みの進行状況がすでに含まれている進行状況を返す onProgress インターフェイスを実装しました。
コードに翻訳すると:

クラスアセット{
    ロード(onProgress) {
        新しいPromise((resolve) => {を返す
            if (typeof onProgress !== 'function') {
                onProgress = (_p) => { };
            }

            loadedCount = 0 とします。
            let totalCount = 10; // 注意: デモ用です
            onLoaded = () => {
                ロードされたカウント++;
                onProgress(読み込まれたカウント / 合計継続時間);
                読み込まれたカウントが合計カウントの場合、解決します。
            };

            約束.すべて(
                this.refAssets.map(asset => asset.load().then(onLoaded))
            );
        });
    }
}

このインターフェースができたので、loadedCount と totalCount のグローバルメンテナンスという形式を引き続き使用すると、扱いが非常に面倒になります。
次にこの記事で紹介するのは回避策です。

原理

基本的な考え方は分割統治です。大きなタスクを複数の小さなタスクに分割し、すべての小さなタスクの進行状況を個別に計算し、最後にすべての小さなタスクの進行状況を結合して全体の進行状況を取得します。
次の図に示すように:

+--------------------------------------------------------------------+
| |
| |
| 総進捗状況 |
| |
| +---------+----------+-----------+----------+---------+---------+ |
| | スクリプト1 | スクリプト2 | テクスチャ1 | テクスチャ2 | モデル1 | モデル2 | |
| | (0~1) | (0~1) | (0~1) | (0~1) | (0~1) | (0~1) | |
| +---------+----------+-----------+----------+---------+---------+ |
| |
| モデル1 |
| +-------------+------------------------+-----------+ |
| | model1.json | マテリアル1 | マテリアル2 | |
| | (0~1) | (0~1) | (0~1) | |
| +------------------------+------------------------+ |
| | テクスチャ1 | テクスチャ2 | |
| | (0~1) | (0~1) | |
| +----------+-------------+ |
| |
| モデル2 |
| +-------------+------------------------+-----------+ |
| | model2.json | マテリアル1 | マテリアル2 | |
| | (0~1) | (0~1) | (0~1) | |
| +------------------------+------------------------+ |
| | テクスチャ1 | テクスチャ2 | |
| | (0~1) | (0~1) | |
| +----------+-------------+ |
| |
+--------------------------------------------------------------------+

この原則に基づいて、すべてのリソースの現在の読み込み進行状況をリストに保存し、onProgress がトリガーされるたびにマージ操作を実行して全体の進行状況を計算することで、進行状況が実装されます。

var 進行 = [
  0, // スクリプト1,
  0, // スクリプト2,
  0, // テクスチャ1,
  0, // テクスチャ2,
  0, // モデル1,
  0, // モデル2
];

関数 onProgress(p) {
    // TODO: progresses[??] = p;
    progresses.reduce((a, b) => a + b, 0) / progresses.length を返します。
}

しかし、ここには難しさがあります。onProgress コールバックがトリガーされたとき、リスト内のどの項目を更新する必要があるかをどのように知るのでしょうか?
JavaScript のクロージャ機能を使用すると、この機能を簡単に実装できます。

var 進捗 = [];
関数add(){
    進行します。push(0);
    var インデックス = progresses.length - 1;

    関数onProgress(p)を返す{
        progresses[インデックス] = p;
        減らす();
    };
}

関数reduce() {
    progresses.reduce((a, b) => a + b, 0) / progresses.length を返します。
}

クロージャを使用してリソースのインデックスを保持します。onProgress がトリガーされると、リスト内の対応する項目の進行状況をインデックスに従って更新できます。正しい進行状況は最終マージ時に計算できます。
残っているのは、すべてのコードを統合してテストすることだけです。

テスト

次のコードを使用して、読み込みプロセス全体をシミュレートできます。

クラスアセット{
    コンストラクター(totalCount) {
        this.loadedCount = 0;
        this.totalCount = 合計カウント;
        this.timerId = -1;
    }

    ロード(onProgress) {
        if (typeof onProgress !== 'function') {
            onProgress = (_p) => { };
        }

        新しいPromise((resolve) => {を返す
            this.timerId = setInterval(() => {
                this.loadedCount++;
                onProgress(this.loadedCount / this.totalCount);
                (this.loadedCount === this.totalCount)の場合{
                    タイマーIDをクリアします。
                    解決する();
                }
            }, 1000);
        });
    }
}

クラス Progress {
    コンストラクター(onProgress) {
        onProgress は、次の式で定義されます。
        this._list = [];
    }

    追加() {
        this._list.push(0);

        定数インデックス = this._list.length - 1;

        戻り値 (p) => {
            this._list[インデックス] = p;
            これをreduce()します。
        };
    }

    減らす() {
        定数 p = Math.min(1, this._list.reduce((a, b) => a + b, 0) / this._list.length);
        this.onProgress(p);
    }
}

const p = 新しい Progress(console.log);
定数asset1 = 新しいアセット(1);
定数asset2 = 新しいアセット(2);
定数asset3 = 新しいアセット(3);
定数asset4 = 新しいアセット(4);
定数asset5 = 新しいアセット(5);

Promise.all([
    アセット1.load(p.add())、
    アセット2.load(p.add())、
    アセット3.load(p.add())、
    アセット4.load(p.add())、
    アセット5.load(p.add())、
]).then(() => console.log('すべてのリソースが読み込まれました'));

/**
  出力 Promise { <state>: "pending" }
  
  0.2 
  0.3 
  0.366666666666666664 
  0.416666666666666663 
  0.456666666666666667 
  0.55666666666666668 
  0.6233333333333333 
  0.6733333333333333 
  0.7133333333333333 
  0.78 
  0.83000000000000001 
  0.8699999999999999 
  0.9199999999999999 
  0.96 
  1 
  すべてのリソースが読み込まれました 
 */

この方法の利点は、loadedCount と totalCount のグローバル管理を回避し、この部分の作業をリソースの内部管理に戻すことができることです。必要なのは、大きなタスクをマージして計算することだけです。

欠点も明らかであり、onProgress インターフェースを統一する必要があります。既存のプロジェクトを進めるのは非常に難しいため、新規プロジェクトや小規模プロジェクトに適しています。

以上がJavaScript進捗管理の詳細です。JavaScript進捗管理の詳細については、123WORDPRESS.COMの他の関連記事にも注目してください!

以下もご興味があるかもしれません:
  • jsは矢印で進行プロセスを実現します
  • JS はプログレスバーの動的な読み込み効果を実現します
  • 進捗バー効果を実現するための JavaScript+CSS
  • JSは制御可能なプログレスバーを実装します
  • jsを使用してシンプルなプログレスバー効果を実現する
  • Node.js はプログレスバー付きの複数ファイルのアップロードを実装します
  • js+HTML5 キャンバスでシンプルな読み込みバー (プログレスバー) 関数を実装する例
  • ネイティブ js を使用して、進行状況を監視するファイルアップロード プレビュー コンポーネントを実装する方法を 3 分で教えます。
  • JS でダウンロード進行状況バーと再生進行状況バーを実装するためのコード

<<:  Windows 10 での MySQL 5.7.21 winx64 のインストールと設定方法のグラフィック チュートリアル

>>:  Nginx操作応答ヘッダー情報の実装

推薦する

MySQL可視化ツールNavicatへの接続方法

Navicatをインストールした後次のエラーが発生する場合があります: Client does no...

7つのMySQL JOINタイプのまとめ

始める前に、これから紹介する JOIN タイプを示すために 2 つのテーブルを作成します。テーブルを...

Nginxホットデプロイメントの実装

目次セマフォNginx ホットデプロイメント上記のブログ投稿に従ってください。ファイアウォールをオフ...

Linux での感嘆符コマンド (!) の使用の概要

序文最近、弊社では mbp の設定をしており、ssh を使うことが多くなりました。複雑なコマンドを書...

MySql エラー 1698 (28000) の解決策

1. 問題の説明: MysqlERROR1698 (28000) の解決方法、新しくインストールされ...

フォームから Vue ElementUI を使用してログイン効果を実装する例

目次1. ElementUIで基本的なスタイルを構築する2. [送信]ボタンをクリックして、アカウン...

Vueプロジェクトでページジャンプを実装する方法

目次1. vue-cli デフォルト プロジェクトを作成する (babel のみを含む) 2. 作成...

JavaScriptプロトタイプチェーンを理解する

目次1. プロトタイプとプロトタイプチェーンの平等関係を理解する2: プロトタイプとプロトタイプ チ...

dockerを使用してTomcatをデプロイし、Skywalkingに接続する

目次1. 概要2. dockerを使用してTomcatをデプロイし、Skywalkingに接続する要...

Windows に Docker をインストールする詳細なチュートリアル

ローカルの MySQL バージョンが比較的低いため、最近 MySQL のバージョンをアップグレードす...

Nginx アップロードファイルのサイズを変更する簡単な方法

オリジナルリンク: https://vien.tech/article/138序文私は、マークダウン...

js は丸で囲まれた数字のリストのサンプルコードを動的に追加します

1. まず本文にulタグを追加します <!-- 順序なしリスト --> <ul i...

MySQL ルート パスワードをリセットするときに発生する「不明な列 'password'」問題を解決する方法

夜にMACの電源を入れたところ、突然ルートアカウントがMySQLに正常にログインできなくなったため、...

jQueryはマウスドラッグ画像機能を実装します

この例では、jQuery を使用してマウス ドラッグ イメージ機能を実装します。まず、ラッパーを設定...

水平ヒストグラムを作成するための MySQL ソリューション

序文ヒストグラムは、RDBMS によって提供される基本的な統計情報です。最も一般的に使用されるのは、...