Stopbyte

How to load resources before starting HTML Web Application?

Hi Coders!

In this article, We will explore how the resources loader (for the web environment) could work in properly way. If this is your first time with the canvas, maybe you could be stucked in the ‘image not yet available error’, a moment in which you try to draw an image on a canvas before the image is fully loaded in the browser memory.

This kind of error is really common and we want to study how to avoid it. Obviously, many cross browser integration libraries, that solve browser compatibility still exists. You do not have to reinvent the wheel. But, our goal is to understand what we have behind these libraries.

Background

This example uses jQuery that is loaded in syncronous way in the head section of the document. Before start, It is very important to understand in which way your target browser handle javascript execution and callback events because not all browser works in the same way.

Our target browsers for this post are Chrome, Firefox and Safari.

##Async is our enemy!
We can simplify the things by the following observations:

  1. When an HTML page is written, it is composed by some sections (head, body) and many other tags like img, style and so forth;

  2. When the browser load a page, it parse all the HTML tag in order to understard in which manner the layout of the page must be rendered.

  3. The browser read the entire content of the page, and it starts the execution of script when it reach a script tag and also start the loading of the image when it reach a img tag. Then, when an img tag is reached, the browser create a new asynchronous request to the web server in order to download it. If during this time the browser reached a script tag, it start immediatly the execution of the code in the script (you can avoid this by using defer attribute).

  4. The only exception to this behavior will occur if we load the script by src tag instead of put it into the page. If we use <script src=…"> the browser will download the script before continue execution.

So, if the script have a reference to the image that is not fully loaded and it try to use the image, an error will occur!

You can create this situation with few line of code:

var ctx = //put here a reference to a canvas
var i1 = new Image ()
i1.src="put-here-link-to-high-quality-image"; // here the browser start download of  the image. be carefull!! Browser will download the image in asyncronous way.
ctx.drawImage (i1,0,0,100,100) // here the error will occur

In order to solve this issue, We need to use the callback infrastructure that javascript and all modern browser expose to us. As first, we must avoiding the start of script execution before all page content is fully loaded.

To do this, We have to interact with window.onload callback. Ideally, we must create the HTML page declaring all the resources and script by the tag avoiding all instruction that can start an execution flow. This mean that we must have a page structure like the following structure.

As you can see, the execution flow start only when all resources needed for page is ready!

<html>
   <head>
      <script src="ref_to_jquery"></script>
      <script src="ref_to_other_script"></script>
  </head>
  <body>
      <h1>....</h1>
      ... ... other tag and resource ... ... 
      <script>
           $(window).ready(function() { 
               // start here the execution flow!
                // ONLY after all async requests is completed!
           });
      </script>
  </body>
</html>

###All seems really good… but…

Imagine that we are building a game. We want that all resources that we need for drawing level are loaded before we will write the first frame.

Obviously, We are not able to add all the resources needed by our game by the img tags :slight_smile:
It is still possible, but if you choose to use this method you must hidden all the img loaded because browser will show them in the page ( but you want to use them on a canvas).

If we try to use previous lines (new Image () and image.src) we will fail drammatically.
For example, we can image to load background, player and enemies in the following ways:

var bg = new Image (); bg.src=...
var enemy = new Image (); enemy.src=...

This way is not correct because all requests will start immediatly and the control will return to script without any lock.

In order to have all resources ready, we need to follow three simple steps:

  1. Inform the browser that we want that the script execution will be executed when all page is loaded.

  2. Start the download of all the resources that we need (images in this examples) before starting execution flow.

  3. Wait until all the image is ready and then start the rendering of them.

This action can be implemented with the following code. We will use an iterative function that iterate all the resources needed and wait until all is ready! We used

// we store all the resources that we need in an associative array
var res_to_load = {};
var res_check = null;
res_to_load["img0"] =_addResource("img","./img0.png","image");
res_to_load["img1"] = _addResource("img1","./img1.png","image");

// We define a service function that return an object that store information
// on a single resource
function _addResource(key,src,type)
{
	
	var toRet = new Object();
	toRet.key = key;
	toRet.src = src;
	toRet.type = type;
	return toRet; 
}

// we request the download of resources by this call. 
// the _callback parameter refer to the function to call when all the resources is ready!
function _loadResources(_callback)
{
    // this function start the download of all resources. take into account that 
    // when you assign .src properties a new async call will be created without locking 
   // executing flow. 
	for (var _r in res_to_load)
	{	
		var rData = res_to_load[_r];
		

		res_to_load[_r].img = new Image();
		res_to_load[_r].ready=false;
		res_to_load[_r].img._r = _r;
            // we start the download of the resources here. we request to the browser 
            // to call _isResourcesReady every time a new resource is ready! 
		res_to_load[_r].img.onload = function() {
			res_to_load[this._r].ready=true;
			_isResourcesReady(_callback);
		};
		res_to_load[_r].img.src=rData.src;
	}

}

Ok! We started the download of the resources and waiting for them! The last thing to do is to write the function that will check if we are ready to start the execution flow of the page!

function _isResourcesReady(_callback)
{
	var isOK = true; // when this function is called we suppose all resources is ready
	for (var _r in res_to_load)
	{
		if (res_to_load[_r].ready!=true) isOK=false;	 // if one of the resources is pending than this function return false
	}
	if (isOK) { 
           // if all the resources is ready, then call the _callback 
		_callback();
		return true;
	}

}

Async now is our best friend!

By this code, in the past, we developed many HTML games so it is enough robust. You can see this code at work on our last pacman game in which all the resources are loaded by it. :slight_smile:

1 Like