为对象中的所有成员调用 setTimeout() - 从不调用第一个成员,而是调用第二个成员。为什么?

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

提问人:JBel 提问时间:8/16/2018 更新时间:8/16/2018 访问量:38

问:

我有一个 3D 数组(而不是 JS 对象),在我在此问题中发布的 SSCCE 中调用,它包含内部数组(而不是对象),每个数组包含多个 url 对象,每个对象都包含一个字段和一个字段。outerArrayurlinterval

对于给定的内部数组(而不是对象),每个数组的字段值将是相同的。这就是它们的分组方式。intervalurl

我的目的是,对于每个内部数组,我定期定期发送一个 AJAX 请求,并且此间隔的值由各个 url 中的字段确定。然后,根据 AJAX 请求的输出,我在 DOM 中进行了一些更改。interval

我从下面给出的代码中预料到这一点,但问题是它确实输入了第一个内部数组的函数(即间隔为 7 的 url),但随后从不调用 setTimeout() 函数,并继续调用下一个内部数组的函数(即间隔为 10 的 url),然后它确实调用了内部,它再次调用了该函数, 这个过程还在继续......next()next()setTimeout()next()

我的问题是,为什么控件从不为第一个内部数组(间隔为 7 的 url)输入 setTimeout()?为什么我会出现这种意外行为?

控制台输出:

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
...

脚本 .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"]);

?>

索引 .php:

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

<body>

</body>
</html>
JavaScript jQuery ajax 闭包 setTimeout

评论


答:

1赞 Ahmed Agiza 8/16/2018 #1

这与 setTimeout 的范围有关,使用您当前的代码时,当执行 setTimeout 时,它会读取 innerArray 的最后一个值,该值指向间隔为 10 的数组。有 2 种方法(至少)可以解决这个问题:

1) 使用立即调用的函数包装器为每个 setTimeout 创建一个单独的作用域,并正确引用 innerArray 变量:

$(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) 使用 ES6 let 关键字为每个迭代创建一个单独的范围:

$(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);
    }

});

您可以查看此内容以了解更多信息: JavaScript 闭包:For 循环中的 setTimeout