0

I found this question, which helped me a lot and I was able to successfully utilize 2 integers with a settimeout(). However, when passing objects it doesn't seem to work.

Full fiddle here.

Code:

$(document).ready(function() {
    var me = {
        x: 0,
        y: 0
    }
    var child = {
        x: 0,
        y: 0
    }
    
    function showxydiff(obj1, obj2, when) {
        var xdiff = (obj1.x - obj2.x);
        var ydiff = (obj1.y - obj2.y);
        $('#' + when).append(xdiff + ':' + ydiff + '... ');
    };
    
    for (var i = 1; i <= 5; i++) {
        child.x = Math.floor((Math.random()*100)+1);
        child.y = Math.floor((Math.random()*100)+1);
        showxydiff(child, me, 'now');
        setTimeout(function (obj1, obj2) {
            return function () {
                showxydiff(obj1, obj2, 'later');
            }
        }(child,me), 500);
    }
});

And here are the results:

now: 57:37... 98:50... 72:87... 66:31... 28:30...

later: 28:30... 28:30... 28:30... 28:30... 28:30...

0

2 Answers 2

1

Your problem is that the same child object is being used for every one of your setTimeout() calls thus the for loop modifies the object that all the setTimeout() callbacks will use. This means they all end up with the same value. This is because objects are passed by reference (no copy is made) so all your callbacks have a reference to the exact same object.

You need to either create a new child object for each setTimeout() or pass just the .x and .y values separately.

for (var i = 1; i <= 5; i++) {
    // create new child object for each time through the loop
    child = {};
    child.x = Math.floor((Math.random()*100)+1);
    child.y = Math.floor((Math.random()*100)+1);
    showxydiff(child, me, 'now');
    setTimeout(function (obj1, obj2) {
        return function () {
            showxydiff(obj1, obj2, 'later');
        }
    }(child,me), 500);
}

I also prefer this method of creating the closure (simpler for me to understand):

for (var i = 1; i <= 5; i++) {
    // create new child object for each time through the loop
    // use more compact javascript literal syntax
    child = {x: Math.floor((Math.random()*100)+1), y: Math.floor((Math.random()*100)+1)};
    showxydiff(child, me, 'now');
    // use an immediately invoking function expression
    // to create a closure to capture the current child object reference
    (function(obj1, obj2) {
        setTimeout(function() {
            showxydiff(obj1, obj2, 'later');
        }, 500);
    })(child, me);
}

Working demo: http://jsfiddle.net/jfriend00/Qbd6V/

Sign up to request clarification or add additional context in comments.

11 Comments

Why doesn't something like this fix the problem: jsfiddle.net/scottbeeson/vTY8U/8 ?
@ScottBeeson - because var a = child does not create a copy - just a reference. So, a and child refer to the exact same object. Objects and Arrays are passed or assigned by reference (not by copy).
The reason I ask is because I'm doing some incremental stuff (child.x += something) and stuff based on i, so creating a new object each time becomes a pain.
I see. And there's no way to override that behavior? var a = child.copy? :D
So I just added a baseX and baseY variable that I increment and I am now declaring the object in each iteration. It is working. I'm going to try the other solution before choosing an answer though.
|
0

Its because of closure. Try this:

$(document).ready(function () {
    var me = {
        x: 0,
        y: 0
    };
    var child = {
        x: 0,
        y: 0
    };

    function showxydiff(obj1, obj2, when) {
        var xdiff = (obj1.x - obj2.x);
        var ydiff = (obj1.y - obj2.y);
        $('#' + when).append(xdiff + ':' + ydiff + '... ');
    };

    function callTimeout(obj1, obj2) {
        setTimeout(function () {
            showxydiff(obj1, obj2, 'later');
        }, 500);
    }

    for (var i = 1; i <= 5; i++) {
        child.x = Math.floor((Math.random() * 100) + 1);
        child.y - Math.floor((Math.random() * 100) + 1);
        showxydiff(child, me, 'now');
        // create a new object for removing referencing
        callTimeout($.extend({}, child), $.extend({}, me));
    }
});

Results:

now:
2:59... 6:96... 34:6... 60:3... 71:54...
later:
2:59... 6:96... 34:6... 60:3... 71:54...

Working JSFiddle: http://jsfiddle.net/vTY8U/11/ (using extend of jquery to create a separate object)

http://jsfiddle.net/vTY8U/10/ (create a local child object)

For more info on closure, go through docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures

5 Comments

Your setTimeout() isn't working. It's executing the function immediately. Try changing the setTimeout() time to 5000. It still comes up immediately.
@jfriend00: Thnx for pointing this out, look at the edited post. There are two ways to do this. One with $.extend() and another by creating a local object.
$.extend() is jQuery's utility function for copying properties from one object to another. This question doesn't mention jQuery so we generally don't use it if it's not asked for or mentioned by tag. But it's easy to copy properties yourself.
Yes, but the JSfiddle link which is provided contains jquery 1.10.1 and he uses jquery as well in $(document).ready().
Fair enough, the OP is using jQuery.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.