Calling setTimeout() for all members in object - Never called for 1st member, and called for 2nd member. Why?

早过忘川 提交于 2019-12-13 23:27:39

问题


I have a 3D array (rather JS object), called outerArray in the SSCCE I am posting here in this question, and it contains inner arrays (rather objects), each of which contains several url objects, each of which contains a url field and an interval field.

For a given inner array (rather object), the value of interval field for each url will be the same. That is how they have been grouped.

My purpose is that , for each inner array, I send an AJAX request periodically at regular intervals, and the value of this interval is determined by the interval field in individual urls. Then on the basis of output of the AJAX request, I make some changes in the DOM.

I expected that from the code given below, but the problem is that it does enter the next() function for the first inner array (i.e. the urls with interval 7), but then never calls the setTimeout() function, and goes on to call the next() function for the next inner array (i.e. the urls with interval 10) for which it then does call the setTimeout() inside which it calls the next() function again, and the process goes on...

My question is that why does the control never enter setTimeout() for the first inner array (urls with interval 7)? Why am I getting this unexpected behavior?

CONSOLE OUTPUT:

Function loop iteration 0 for urls with interval 7

Function loop iteration 0 for urls with interval 10

setTimeout() called for urls with interval 10
Success for AJAX request for urls with interval 10

Function loop iteration 1 for urls with interval 10

setTimeout() called for urls with interval 10
Success for AJAX request for urls with interval 10

Function loop iteration 1 for urls with interval 10

setTimeout() called for urls with interval 10
Success for AJAX request for urls with interval 10

Function loop iteration 2 for urls with interval 10

setTimeout() called for urls with interval 10
Success for AJAX request for urls with interval 10

Function loop iteration 2 for urls with interval 10
...

scripts.js:

$(document).ready(function() {

    var outerArray = {
        0 : {
            0 : {
                url: "abc.example.com",
                interval: 7
            },
            1 : {
                url: "def.example.com",
                interval: 7
            }
        },

        1 : {
            0 : {
                url: "ghi.example.com",
                interval: 10
            }
        }
    };




    for (var innerArrayKey in outerArray) {
        var innerArray = outerArray[innerArrayKey];

        (function next(index) {
                console.log("Function loop iteration " + index + "for urls with interval " + innerArray[0]["interval"]);//check
                setTimeout(function() {
                    console.log("setTimeout() called for urls with interval " + innerArray[0]["interval"]);//check
                    $.ajax({
                        url: "http://xxx.yyy.xx.yy/testAsynchronousJSRequests/ajax.php",
                        method: "post",
                        data: { innerArray : innerArray },
                        success: function(dataReturned, stringStatus, jqXHR) {
                            console.log("Success for AJAX request for urls with interval " + innerArray[0]["interval"]);//check
                            next(index+1);
                        },
                        error: function(jqXHR, stringStatus, stringExceptionThrown) {
                            console.log("Error in AJAX request " + innerArray[0]["interval"]);//check
                        }
                    });
                }, (innerArray[0]["interval"]*1000) );

        })(0);
    }

});

ajax.php:

<?php 

print_r($_POST["innerArray"]);

?>

index.php:

<!DOCTYPE html>
<html>
<head>
    <script src="jquery-3.3.1.min.js"></script>
    <script src="scripts.js"></script>
</head>

<body>

</body>
</html>

回答1:


This is related to the scope of setTimeout, with your current code what happens is that when setTimeout gets executed it reads the last value for innerArray which points to the array of interval 10. There are 2 ways (at least) to solve this:

1) Using immediately invoked function wrapper to create a separate scope for each setTimeout with proper reference to the innerArray variable:

$(document).ready(function() {

    var outerArray = {
        0: {
            0: {
                url: "abc.example.com",
                interval: 7
            },
            1: {
                url: "def.example.com",
                interval: 7
            }
        },

        1: {
            0: {
                url: "ghi.example.com",
                interval: 10
            }
        }
    };




    for (var innerArrayKey in outerArray) {
        var innerArray = outerArray[innerArrayKey];

        (function next(index) {
            console.log("Function loop iteration " + index + " for urls with interval " + innerArray[0]["interval"]); //check
            setTimeout(function(innerArray) {
                return function() {
                    console.log("setTimeout() called for urls with interval " + innerArray[0]["interval"]); //check
                    $.ajax({
                        url: "http://xxx.yyy.xx.yy/testAsynchronousJSRequests/ajax.php",
                        method: "post",
                        data: {
                            innerArray: innerArray
                        },
                        success: function(dataReturned, stringStatus, jqXHR) {
                            console.log("Success for AJAX request for urls with interval " + innerArray[0]["interval"]); //check
                            next(index + 1);
                        },
                        error: function(jqXHR, stringStatus, stringExceptionThrown) {
                            console.log("Error in AJAX request " + innerArray[0]["interval"]); //check
                        }
                    });
                }
            } (innerArray), (innerArray[0]["interval"] * 1000));

        })(0);
    }

});

2) Using ES6 let keyword which creates a separate scope for each iteraion:

$(document).ready(function() {

    var outerArray = {
        0: {
            0: {
                url: "abc.example.com",
                interval: 7
            },
            1: {
                url: "def.example.com",
                interval: 7
            }
        },

        1: {
            0: {
                url: "ghi.example.com",
                interval: 10
            }
        }
    };




    for (var innerArrayKey in outerArray) {
        let innerArray = outerArray[innerArrayKey];

        (function next(index) {
            console.log("Function loop iteration " + index + " for urls with interval " + innerArray[0]["interval"]); //check
            setTimeout(function() {
                console.log("setTimeout() called for urls with interval " + innerArray[0]["interval"]); //check
                $.ajax({
                    url: "http://xxx.yyy.xx.yy/testAsynchronousJSRequests/ajax.php",
                    method: "post",
                    data: {
                        innerArray: innerArray
                    },
                    success: function(dataReturned, stringStatus, jqXHR) {
                        console.log("Success for AJAX request for urls with interval " + innerArray[0]["interval"]); //check
                        next(index + 1);
                    },
                    error: function(jqXHR, stringStatus, stringExceptionThrown) {
                        console.log("Error in AJAX request " + innerArray[0]["interval"]); //check
                    }
                });
            }, (innerArray[0]["interval"] * 1000));

        })(0);
    }

});

You may check this to understand more: JavaScript Closures: setTimeout Inside a For Loop



来源:https://stackoverflow.com/questions/51874037/calling-settimeout-for-all-members-in-object-never-called-for-1st-member-an

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!