CodeMoose
CodeMoose

Reputation: 3025

How to use "closure" to assign objects asynchronously?

EDIT: The kind people of this site linked me to a QA that seems related to the problem described below.

JavaScript closure inside loops – simple practical example

While I appreciate the prompt response, and I can understand the need to prevent duplicate content, it leaves me in a pickle - I'm not savvy enough to understand how to repurpose the linked answers to solve my particular problem.

I've modified the question to be more specific - please consider helping me rectify my inexperience this time, instead of leaving me to fend for myself.


Working on an application (read: game), and ran into what I'm sure is a common problem. I have a list of assets that will be loaded asynchronously, each of which needs to reference a specific object once created.

See code below. When an image loads, it should create a Kinetic.Image object and associate it back to the this.enemies[n] that has the correct image URL. this.enemies is cardinal, the assets/objects need to stay in that order - so I can't just push the objects to an array as they're created. The list is also variable-length, so I can't just hard-code things out. I even tried referencing the i from the loop in an act of desperation, and (not surprisingly) it doesn't work.

I tried googling things like "asynchronous assignment", without much success. Here's the question - when 2.png loads and creates its object, how do I use closure to assign it back to the object containing asset: "2.png"? I understand that the linked question is related, but it says nothing about how to execute it when dealing with async loading.

Here's the code:

var Battle = {

    ...

    start: function(){
        this.enemies = [
            { asset: "1.png" },
            { asset: "2.png" },
            { asset: "3.png" }
        ];

        for(i in this.enemies){
            var eImg = new Image();
            eImg.onload = function(){
                Battle.enemies[i].obj = new Kinetic.Image({
                    image: eImg,
                    x: 40, y: 40
                });
            };
            eImg.src = "/game/assets/battle/"+this.enemies[i].asset;
        }
    }
};

Upvotes: 0

Views: 49

Answers (1)

Rob Lourens
Rob Lourens

Reputation: 16109

This is basically what they are suggesting:

function makeCallback(enemy, eImg) {
    enemy.obj = new Kinetic.Image({
        image: eImg,
        x: 40, y: 40
    });
}

var Battle = {

    ...

    start: function(){
        this.enemies = [
            { asset: "1.png" },
            { asset: "2.png" },
            { asset: "3.png" }
        ];

        for(i in this.enemies){
            var eImg = new Image();
            eImg.onload = makeCallback(this.enemies[i], eImg);
            eImg.src = "/game/assets/battle/"+this.enemies[i].asset;
        }
    }
};

With your code, when the function is executed, i is whatever its value is at that time, not at the time the function was defined. Use a function to isolate enemy and eImg and close them inside the function object.

Upvotes: 1

Related Questions