Preface When we write programs, we often encounter the need to display progress, such as loading progress, uploading progress, etc. question I am writing a WebGL application and need to calculate the loading progress during the application preloading phase. +-------------------------------------------------------------+ | | | resources | | | | +----------+ +-----------------+ +-----------------+ | | | script1 | | model1 | | model2 | | | +----------+ | | | | | | | -------------+ | | -------------+ | | | +----------+ | |model1.json | | | |model2.json | | | | | script2 | | +------------+ | | +------------+ | | | +----------+ | | | | | | | +------------+ | | +------------+ | | | +----------+ | | material1 | | | | material1 | | | | | texture1 | | | +--------+ | | | | +--------+ | | | | +----------+ | | |texture1| | | | | |texture1| | | | | | | +--------+ | | | | +--------+ | | | | +----------+ | | +--------+ | | | | +--------+ | | | | | texture2 | | | |texture2| | | | | |texture2| | | | | +----------+ | | +--------+ | | | | +--------+ | | | | | +------------+ | | +------------+ | | | | | | | | | | +------------+ | | +------------+ | | | | | material2 | | | | material2 | | | | | +------------+ | | +------------+ | | | +-----------------+ +-----------------+ | | | +-------------------------------------------------------------+ There is a premise here: when loading a resource, it must be ensured that the resource and the resources it references are all loaded before the loading is considered complete. class Asset { load(onProgress) { return new Promise((resolve) => { if (typeof onProgress !== 'function') { onProgress = (_p) => { }; } let loadedCount = 0; let totalCount = 10; // NOTE: just for demo let onLoaded = () => { loadedCount++; onProgress(loadedCount / totalCont); if (loadedCount === totalCount) resolve(); }; Promise.all( this.refAssets.map(asset => asset.load().then(onLoaded)) ); }); } } Now that we have this interface, if we continue to use the form of global maintenance of loadedCount and totalCount, it will be quite troublesome to handle. principle The basic idea is divide and conquer. Split a large task into multiple small tasks, then calculate the progress of all small tasks separately, and finally merge the progress of all small tasks to get the total progress. +--------------------------------------------------------------------+ | | | | | total progress | | | | +---------+---------+----------+----------+--------+--------+ | | | script1 | script2 | texture1 | texture2 | model1 | model2 | | | | (0~1) | (0~1) | (0~1) | (0~1) | (0~1) | (0~1) | | | +---------+---------+----------+----------+--------+--------+ | | | | model1 | | +-------------+-----------------------+-----------+ | | | model1.json | material1 | material2 | | | | (0~1) | (0~1) | (0~1) | | | +------------------------+------------------------+ | | | texture1 | texture2 | | | | (0~1) | (0~1) | | | +----------+------------+ | | | | model2 | | +-------------+-----------------------+-----------+ | | | model2.json | material1 | material2 | | | | (0~1) | (0~1) | (0~1) | | | +------------------------+------------------------+ | | | texture1 | texture2 | | | | (0~1) | (0~1) | | | +----------+------------+ | | | +--------------------------------------------------------------------+ Based on this principle, the progress is implemented by saving the current loading progress of all resources through a list, and then performing a merge operation each time onProgress is triggered to calculate the total progress. var progresses = [ 0, // script1, 0, // script2, 0, // texture1, 0, // texture2, 0, // model1, 0, // model2 ]; function onProgress(p) { // TODO: progresses[??] = p; return progresses.reduce((a, b) => a + b, 0) / progresses.length; } But there is a difficulty here. When the onProgress callback is triggered, how do we know which item in the list should be updated? var progresses = []; function add() { progresses.push(0); var index = progresses.length - 1; return function onProgress(p) { progresses[index] = p; reduce(); }; } function reduce() { return progresses.reduce((a, b) => a + b, 0) / progresses.length; } Use closures to retain the index of resources. When onProgress is triggered, the progress of the corresponding item in the list can be updated according to the index. The correct progress can be calculated during the final merge. testWe can use the following code to simulate the entire loading process: class Asset { constructor(totalCount) { this.loadedCount = 0; this.totalCount = totalCount; this.timerId = -1; } load(onProgress) { if (typeof onProgress !== 'function') { onProgress = (_p) => { }; } return new Promise((resolve) => { this.timerId = setInterval(() => { this.loadedCount++; onProgress(this.loadedCount / this.totalCount); if (this.loadedCount === this.totalCount) { clearInterval(this.timerId); resolve(); } }, 1000); }); } } class Progress { constructor(onProgress) { this.onProgress = onProgress; this._list = []; } add() { this._list.push(0); const index = this._list.length - 1; return (p) => { this._list[index] = p; this.reduce(); }; } reduce() { const p = Math.min(1, this._list.reduce((a, b) => a + b, 0) / this._list.length); this.onProgress(p); } } const p = new Progress(console.log); const asset1 = new Asset(1); const asset2 = new Asset(2); const asset3 = new Asset(3); const asset4 = new Asset(4); const asset5 = new Asset(5); Promise.all([ asset1.load(p.add()), asset2.load(p.add()), asset3.load(p.add()), asset4.load(p.add()), asset5.load(p.add()), ]).then(() => console.log('all resources loaded')); /** Output Promise { <state>: "pending" } 0.2 0.3 0.366666666666666664 0.416666666666666663 0.456666666666666667 0.55666666666666668 0.6233333333333333 0.6733333333333333 0.7133333333333333 0.78 0.8300000000000001 0.8699999999999999 0.9199999999999999 0.96 1 all resources loaded */ The advantage of this method is that it can avoid the global management of loadedCount and totalCount, and return this part of the work to the internal management of resources. All it has to do is merge and calculate large tasks. The disadvantages are also obvious, and the onProgress interface needs to be unified. It is very difficult to advance in existing projects, so it is more suitable for new projects or small projects. The above is the details of JavaScript progress management. For more information about JavaScript progress management, please pay attention to other related articles on 123WORDPRESS.COM! You may also be interested in:
|
<<: MySQL 5.7.21 winx64 installation and configuration method graphic tutorial under Windows 10
>>: Implementation of Nginx operation response header information
Table of contents Preface: Detailed introduction:...
Background Replication is a complete copy of data...
1. Install JDK 1.1 Check whether the current virt...
This article example shares the specific code of ...
TRUNCATE TABLE Deletes all rows in a table withou...
First, take a look at Alibaba Cloud's officia...
If you want to become a Linux master, then master...
Table of contents 1. Introduction to label statem...
In the table header, you can define the light bor...
1. Check the character set of the default install...
illustrate MySql Community Edition supports table...
Table of contents What is ref How to use ref Plac...
Use navicat to test and learn: First use set auto...
1. Add in package.json "main": "el...
1. Change password 1. Modify the password of ordi...