0

Back again asking silly questions... :)

I've made a few functions, one for storing images, one for retrieving and one for checking if images are loaded. Images are stored in an array like this:

tiles = [ ["ID", "Image Object", "Loaded boolean"] ];

However, the order in which Javascript runs my code boggles me. I've added several console.logs to see what's going on in the code and this is what I'm getting:

    START
    Storing http://funxion.wippiespace.com/js/mario/question.gif...  
    Storing http://funxion.wippiespace.com/js/mario/wall.gif...  
    Storing http://funxion.wippiespace.com/js/mario/floor.gif...  
    Map.draw initiated  
    Checking if all images are loaded...  
    Amount of tile images = 3  
    Checking [0]  
    [0]: q,[object HTMLImageElement],false is not loaded  
    Images not loaded!  
    http://funxion.wippiespace.com/js/mario/question.gif stored!  
    http://funxion.wippiespace.com/js/mario/wall.gif stored!  
    http://funxion.wippiespace.com/js/mario/floor.gif stored!  

I dont understand why the loaded=true is the last thing in there. Also, I've tried putting a setTimeout(this.draw, 1000); in the Map.prototype.draw so there would be a 1sec delay before checking allImgLoaded again, that doesn't work. Without the loading check drawing works fine. Also, Start() is called in .

The code:

    var canvas = document.getElementById("surface");
    var ctx = canvas.getContext("2d");

    function Map()
    {
        this.tiles = [];
    }
    Map.prototype.draw = function(tileid)
    {
        console.log("Map.draw initiated");
        if (!allImgLoaded(this.tiles))
        {
            console.log("Images not loaded!");
        }
        else
        {
            console.log("All good, proceeding.");
            var img = this.getImg(tileid);
            ctx.drawImage(img, 100, 100);
        }
    }
    Map.prototype.storeImg = function(identifier, imgSrc)
    {
        var nextIndex = this.tiles.length;
        var tile = [identifier, new Image(), false];
        tile[1].src = imgSrc;
        console.log("Storing " + imgSrc + "...");
        tile[1].onload = function()
        {
            tile[2] = true;
            console.log(this.src + " stored!");
        }
        this.tiles[nextIndex] = tile;

    }
    Map.prototype.getImg = function(identifier)
    {
        for (var i in this.tiles)
        {
            console.log("Checking index " + i + " for " + identifier + "...");
            if (this.tiles[i][0] === identifier)
            {
                console.log("Found " + this.tiles[i][1] + "! Returning it now.");
                return this.tiles[i][1];
            }
        }
    }

    function allImgLoaded(array)
    {
        console.log("Checking if all images are loaded...");
        console.log("Amount of tile images = " + array.length);
        for (var i in array)
        {
            console.log("Checking ["+i+"]");
            if(array[i][2] === false)
            {
                console.log("["+i+"]: " + array[i] + " is not loaded");
                return false;
            }
        }
        console.log("All loaded!");
        return true;
    }
    function Start()
    {
        console.log("START");
        var mappi = new Map();
        mappi.storeImg("q", "http://funxion.wippiespace.com/js/mario/question.gif");
        mappi.storeImg("w", "http://funxion.wippiespace.com/js/mario/wall.gif");
        mappi.storeImg("f", "http://funxion.wippiespace.com/js/mario/floor.gif");
        mappi.draw("w");
    }
fnx
  • 369
  • 1
  • 5
  • 16
  • Image objects have a `complete` property that gives a boolean value based on whether the image is loaded or not. You don't need to keep track of that yourself. – Pointy Oct 19 '12 at 21:37
  • As an alternative to jfriend00's code, you can check my function here http://stackoverflow.com/questions/12333634/load-images-during-page-startup/12333832#12333832 in case you want to access the images by their names instead of numerical indexes, it also supports other events besides onload – Delta Oct 19 '12 at 21:48

1 Answers1

0

You don't seem to understand that the loading of images is asynchronous. It happens in the background while your other javascript continues to run. You start the operation, your other code continues to run and some time later the images finish loading the onload callback function gets called.

If you want to run an operation AFTER an image is loaded, you need to start that operation from the onload handler for that image. Right now your code starts the loading of all the images and then you immediately call mappi.draw() before the images are done loading. More specifically .storeImg() just starts the loading of an image. The actual loading of the image is not done until some time later (as you can see from your console.log() trace.

If you want to preload a bunch of images and then trigger an operation when all the images are loading, you can use code like is found in this answer: Image preloader javascript that supports events which will start the loading of a bunch of images and then call a callback when they are all loaded. You could then call your .draw() method from that callback. Using callbacks like this is part of asynchronous programming in javascript and is required anytime something is being loaded over the network (an image, an ajax call, etc...).

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Thanks for the quick answer, now I understand javascript a bit more :) – fnx Oct 19 '12 at 22:21
  • @fnx - for a little more learning, these descriptions of javascript's event queue and single threading might help you also: http://stackoverflow.com/questions/7575589/how-does-javascript-handle-ajax-responses-in-the-background/7575649#7575649 and http://stackoverflow.com/questions/8611145/race-conditions-with-javascript-event-handling/8611469#8611469. All the asynchronous events (ajax, image loading, etc...) work similarly. – jfriend00 Oct 19 '12 at 22:58