Implementation of Sys.scriptloader and JS loading progress bar

Source: Internet
Author: User
Tags array exit eval reference return sleep variable
js| Loading

Today, I was asked how a 163-mailbox-like JavaScript load progress bar was implemented.

I do not know, but it is not difficult to achieve, because <script/> have onload and onreadystatechange. And yes, we have atlas.

There is a class in Atlas: Sys.scriptloader, which is the function of loading multiple script files sequentially in a page. Before implementing, analyze the code for this class first.

1sys.scriptloader = function () {


2


3//All script reference array of objects.


4 var _references;


5//callback function executed after all script finishes loading.


6 var _completioncallback;


7//The context (parameter) provided when the callback function is executed.


8 var _callbackcontext;


9


10//The HTTP Element (&lt;script/&gt;) of the script that is currently being loaded.


var _currentloadingreference;


12//callback function called after the current script load completes.


var _currentonscriptload;


14


//Scriptloader unique method, passing in three parameters, the parameter meaning no longer repeat.


this.load = function (references, completioncallback, Callbackcontext) {


_references = references;


_completioncallback = CompletionCallback;


_callbackcontext = Callbackcontext;


20


loadreferences ();


22}


23


24//Start loading reference.


The function loadreferences () {


26//If a script is currently being loaded.


27//This means that this method is not invoked for the first time, but is loaded in a script


28//finished before being invoked to load the next script.


if (_currentloadingreference) {


30//View the current SCRIPT element Readystate,ie under complete,


31//Other browsers such as FF are loaded (FF does not actually have this attribute,


32//But the following code will set it to loaded).


33//If the load fails, exit.


if ((_currentloadingreference.readystate!= ' loaded ') &amp;&amp;


(_currentloadingreference.readystate!= ' complete ')) {


return;


37}


/Else {


39//Enter this branch, indicating that the load was successful.


40


41//If the OnLoad function is defined by the current script.


(_currentonscriptload) {


43//Call through Eval (this is a troublesome place).


eval (_currentonscriptload);


45//set to NULL to release resources.


_currentonscriptload = null;


47}


48


49//Set related events to NULL to ensure that resources are freed.


if (Sys.Runtime.get_hostType ()!= Sys.HostType.InternetExplorer) {


51//If the current browser is not IE, see the following code


52//will find the OnLoad event defined for &lt;script/&gt;.


_currentloadingreference.onload = null;


54}


/Else {


56//If it is IE, see the following code will be found in order to


//&lt;script/&gt; Defines the onreadystatechange event.


_currentloadingreference.onreadystatechange = null;


59}


60


61//FINAL release of the current &lt;script/&gt; Reference.


_currentloadingreference = null;


63}


64}


65


66//If there is no loaded script.


if (_references.length) {


68//OUT queue.


Reference var = _references.dequeue ();


70//Create &lt;script/&gt;


var scriptelement = document.createelement (' script ');


72//sets the current &lt;script/&gt; and the callback function that is currently loaded successfully.


_currentloadingreference = scriptelement;


_currentonscriptload = reference.onscriptload;


75


The IF (Sys.Runtime.get_hostType ()!= Sys.HostType.InternetExplorer) {


77//If it is not IE, then set the attribute readystate for &lt;script/&gt;,


78//and use onload event.


scriptelement.readystate = ' loaded ';


scriptelement.onload = loadreferences;


81}


else {


83//If it is IE, then use the onReadyStateChange event.


scriptelement.onreadystatechange = loadreferences;


85}


scriptelement.type = ' text/javascript ';


scriptelement.src = Reference.url;


88


89//Add &lt;script/&gt; to Dom


var headelement = document.getelementsbytagname (' head ') [0];


Headelement.appendchild (scriptelement);


92


return;


94}


95


96//If executed here, show that all the script is finished loading.


97//If you define a callback function that executes after all the script is loaded,


98//Then execute and release resources.


if (_completioncallback) {


var completioncallback = _completioncallback;


the var callbackcontext = _callbackcontext;


102


_completioncallback = null;


_callbackcontext = null;


105


CompletionCallback (Callbackcontext);


107}


108


109 _references = null;


110}


111}


112sys.scriptloader.registerclass (' Sys.scriptloader ');


can see that the Sys.scriptloader loading script is to add &lt;script/&gt; elements to &lt;header/&gt; by code in turn. In fact, it's been used very little in Atlas.

In fact, the Sys.scriptloader code is very simple and the annotations I add are more like the superfluous. It is worth noting that all resources are released as much as possible. Note in particular the code starting at line 99th, where the if body first retains two global variables with a temporary variable and then releases the global variable. The goal is to avoid memory leaks that occur when CompletionCallback throws an exception at execution time, even if it is only one out of 10,000 possible. The more JavaScript, the more likely to cause memory leaks, in writing JS code is best to pay attention to this aspect of the problem.

Then explain the first parameter of the Load method references, the original thought of this sys.reference class array, the result found that the difference is very far. Anyway, take a look at the code for that class.

 1sys.reference = function () {
 2
 3    var _component;
 4    var _onload;
 5   
 6    this.get_component = function () {
 7         return _component;
 8   }
 9    this.set_component = function (value) {
10         _component = value;
11   }
12   
13    this.get_onscriptload = function () {
14         return _onload;
15   }
16    this.set_onscriptload = function (value) {
17        _onload = value;
18   }
19   
20    this.dispose = function () {
21         _component = null;
22&Nbsp;  }
23   
24    this.getdescriptor = function () {
25         var td = New Sys.typedescriptor ();
26       
27        td.addProperty (' Component ', Object);
28        td.addproperty (' onscriptload ', String);
29        return TD;
30   }
31}
32sys.reference.registersealedclass (' sys.reference ', null, Sys.itypedescriptorprovider, Sys.IDisposable);
33sys.typedescriptor.addtype (' script ', ' reference ', sys.reference);
Care for the code of the Sys.scriptloader class, each element of the reference array is actually simply "{URL: http://www.sample.com/sample.js", Onscriptload: " An object in the form of alert (1) "}". Still, it's easy to use JSON to construct such an array.

Here, I think we should also think of how to use Sys.scriptloader easy to make JS loaded progress bar. But now that I've written it, I'm going to make it a simple implementation.

The first is the ASPX file.

1&lt;%@ Page language= "C #"%&gt;


2


3&lt;! DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 transitional//en" "Http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd "&gt;


4


5&lt;script runat= "Server" &gt;


6


7&lt;/script&gt;


8


9&lt;html xmlns= "http://www.w3.org/1999/xhtml" &gt;


10&lt;head runat= "Server" &gt;


&lt;title&gt;load scripts&lt;/title&gt;


&lt;script language= "JavaScript" &gt;


function Load ()


14 {


document.getElementById ("Bar"). Style.width = "0px";


var scripts = new Array ();


for (var i = 0; i &lt; 8; i++)


18 {


var s = new Object ();


var sleep = Math.Round (Math.random () * 400)) + 100;


S.url = "script.ashx?sleep=" + Sleep + "&amp;t=" + math.random ();


S.cost = sleep;


Scripts.push (s);


24}


25


Jeffz.Sample.LoadScripts.load (scripts);


27}


&lt;/script&gt;


29&lt;/head&gt;


30&lt;body style= "font-family:arial;" &gt;


&lt;form id= "Form1" runat= "Server" &gt;


&lt;div&gt;


&lt;atlas:scriptmanager id= "ScriptManager1" runat= "Server" &gt;


&lt;Scripts&gt;


&lt;atlas:scriptreference path= "Js/loadscripts.js"/&gt;


&lt;/Scripts&gt;


Notoginseng &lt;/atlas:ScriptManager&gt;


38


Progress Bar:


&lt;div style= "Border:solid 1px black;" &gt;


&lt;div id= "bar" style= "height:20px; width:0%; background-color:red; " &gt;&lt;/div&gt;


&lt;/div&gt;


&lt;input type= "button" value= "Load"/&gt;


&lt;div id= "message" &gt;&lt;/div&gt;


&lt;/div&gt;


&lt;/form&gt;


47&lt;/body&gt;


48&lt;/html&gt;

The
is very simple. Use two div to make one of the simplest progress bars. The load () function is invoked when the button is clicked. The function randomly generates a script link and generates a scripts array of 8 elements. The scripts array is formatted as follows:

1var scripts =
2[
3 {URL: "Http://www.sample.com/sample1.js", cost:costofloading1},
4 {URL: "Http://www.sample.com/sample2.js", cost:costofloading2},
5 {URL: "Http://www.sample.com/sample3.js", Cost:costofloading3}
6];
The URL attribute for each element does not have to be said, and the function of the cost is to indicate the value of the time spent loading the file. This value has no units, only the proportion of this value in total consumption. Other than that You can see that there is a script.ashx that simulates a long script load that sleeps a thread for a period of time based on the value of sleep in QueryString (for the following T, the goal is to avoid the browser's cache by changing the QueryString), which is almost No code, you can see the implementation of it in the sample download. Finally, by calling the Jeffz.Sample.LoadScripts.load method to load, this involves the following code, Loadscripts.js:

1type.registernamespace (' jeffz.sample ');


2


3jeffz.sample.loadscripts = new function ()


4{


5 var totalcost = 0;


6 var scriptloader = new Sys.scriptloader ();


7


8 this.load = function (scripts)


9 {


(jeffz.sample.__onscriptload!= null)


11 {


throw new Error ("in progress");


13}


14


totalcost = 0;


jeffz.sample.__onscriptload = onscriptload;


var references = new Array ();


18


var loadedcost = 0;


for (var i = 0; i &lt; scripts.length; i++)


21 {


TotalCost + = Scripts[i].cost;


Loadedcost + = Scripts[i].cost;


24


var ref = Createreference (Scripts[i].url, loadedcost);


26


References.push (ref);


28}


29


scriptloader.load (references, oncomplete);


31}


32


function createreference (URL, loadedcost)


34 {


var ref = new Object ();


ref.url = URL;


ref.onscriptload = "jeffz.sample.__onscriptload (' + URL +" ', "+ Loadedcost +") ";


return ref;


39}


40


OnComplete ()


42 {


jeffz.sample.__onscriptload = null;


44}


45


function onscriptload (URL, loadedcost)


47 {


var progress = 100.0 * LOADEDCOST/TOTALCOST;


document.getElementById ("Bar"). Style.width = progress + "%";


document.getElementById ("message"). InnerHTML + = ("&lt;strong&gt;" + URL + &lt;/strong&gt; "+" loaded.&lt; br/&gt; ");


51}


52}


Hey, there seems to be absolutely no need to explain the code unnecessarily. So far, a simple script loading progress bar is complete, quite simple. The code can click here to download, or click here to view the effect.

But is that the end of the story? In fact, I'm not very satisfied with the solution, although it should be enough for most cases. Notice that I've made the Jeffz.Sample.LoadScripts implementation a singleton, which means there's no other instance like it. And at the very beginning of the Load method it is judged to be loading, and if so, an exception will be thrown. Implementation of such a "single-threaded" loading, the direct reason is limited by Sys.scriptloader implementation.

Look at line 44th of the Sys.scriptloader code, which uses eval to "malign" the callback when the script load completes. This is actually a very uncomfortable implementation for the developer, because Eval makes it impossible to pass a reference to a function as a callback function. The only thing you can do is to give the "root code" as a string to Sys.scriptloader. Although it is still possible to implement "concurrent" script loading through Sys.scriptloader (plainly, you can build a queue like sys.scriptloader), the amount of code naturally goes up and the complexity of the development increases.

In addition, Sys.scriptloader to load a script error is not prompted, but the direct exit, this is not very ideal.

But I think that this "single-threaded" script load is enough for most situations. And if there really is a "special" requirement, is it not easy to write a new one for the vast majority of developers, referring to Sys.scriptloader, a clear example of this?







Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.