JS循环中的异步函数

时间:2015-04-20 10:11:54

标签: javascript jquery ajax get

我有一些PHP的经验,但我开始使用javascript和jquery。我正在研究我的第一个项目。我认为脚本编写脚本,这与PHP之间几乎没有区别。好吧,我错了。我第一次看到代码中第一个执行的东西最后一次!

请看看这个函数是什么意思是获取svg并将它们存储在json对象中以便以后用作内联svg

var svgIcons = { "arrow_left": "", "arrow_right":"",  } //json object with empty values

    this.getIcons = function() {

    for (var icon_name in svgIcons) {
        if (svgIcons.hasOwnProperty(icon_name)) {
            var url=PHP.plugin_url+'/includes/icons/'+icon_name+'.svg';
            jQuery.get(url, function(data) { 

            svgIcons[icon_name]=data;

            console.log('iterating');
            console.log(svgIcons[icon_name]); //outputs svg
            });
        }
    }

    console.log('this should be after iteration');
    console.log(svgIcons["arrow_left"]); //empty

}
this.getIcons(); //called at object initialization

但输出是:

this should be after iteration

iterating
#document (and svg inside it)
iterating
#document (and svg inside it)

这种订单变更的原因是什么?是get()函数吗?我该如何避免这种情况?

2 个答案:

答案 0 :(得分:1)

jQuery.get是异步的。您正在回调AJAX调用的回调内部,以便在AJAX调用完成时执行。

AJAX回调,setTimeoutsetInterval是一些异步Javascript函数。您可能会发现一些有用的线程:

编辑:是的,函数调用在任何回调内容发生之前结束。基本上,JS的执行是线性的,只要调用它们就将函数放在调用堆栈上。在调用堆栈上,它们逐行逐行执行。但是,当其中一行调用异步函数(如setTimeout或AJAX)时,当前执行会将异步函数放在调用堆栈上并立即返回以完成自身。如下所示:

function myFunc(){
   console.log('a');
   setTimeout(function(){
       console.log('b');
   },0)
   console.log('c');
}
myFunc();

总是会记录:

a
c
b

...即使setTimeout为0。

因此,在您的情况下,必须发生的是您将异步回调中的AJAX接收数据分配给svgIcons[icon_name](显然),而使用对象{{1}的代码的其余部分在顺序/正常执行中。您要么必须移动在异步回调中使用该对象的代码,要么使用promises(基本上承诺是在异步调用完成后执行的函数)。

第二次修改:因此,您无法在回调中设置svgIcons的原因与我在评论中提到的内容有关。当调用同步函数时,它们被放置在当前堆栈的顶部并立即执行,返回调用函数之前。因此,如果您在循环中调用 sync 函数:

svgIcons[icon_name]

同步function outer(){ function inner(){ console.log(i); } for(var i=0;i<3;i++) inner(); } outer(); 函数将立即执行 in 每个循环,并且可以访问当前值inner,因此它将输出{{1} },i0(按预期方式)。

但是,如果1是异步的,例如

2

然后你会得到inner function outer(){ for (var i=0;i<3;i++) setTimeout(function(){console.log(i)},0); } 3作为输出! 这是因为循环已经完成,包括最终的3

所以现在我认为您可以看到代码的问题。最多调用3您可以访问{em>当前i++值,但是一旦我们进入异步回调,当前值就会消失,并被最后一个值替换掉,因为在执行任何回调之前循环已经完成。

尝试这样的事情:

jQuery.get

这使用icon_name方法,该方法是数组(MDN reference)的原生方法。在传递给var svgIcons = {} var props = ["arrow_left","arrow_right"]; this.getIcons = function() { props.forEach(function(prop){ var url=PHP.plugin_url+'/includes/icons/'+prop+'.svg'; jQuery.get(url, function(data) { svgIcons[prop]=data; var fullyLoaded = false; for(var i=0;i<props.length;i++) { if(!svgIcons.hasOwnProperty(props[i])){ fullyLoaded = false; break; } else fullyLoaded = true; } // end for loop if(fullyLoaded) callMyFunctionWhereIUseSvgIconsData(); }); //end jQuery.get() });//end forEach } this.getIcons() 的函数内部,第一个参数始终是数组的当前元素(我将其命名为forEach)。因此没有杂乱的循环或forEach,并且每个执行的函数都可以访问自己的prop属性。

然后, AJAX回调中,我将当前i分配给收到的数据,然后遍历所有属性以检查prop对象是否已收到属性。因此,只有在执行了所有回调并且全局prop已收到所有属性和数据后,svgIcons才会评估为true。因此,您可以现在调用使用该对象的函数。

希望这有帮助,请随时进一步询问或告诉我控制台是否会出错。

答案 1 :(得分:0)

任何ajax调用都是异步的,因此可以在进行ajax调用时运行它。如果您想在完成所有通话后拨打电话,请尝试以下操作:

var svgIcons = { "arrow_left": "", "arrow_right":"",  } //json object with empty values
var executing = 0;
this.getIcons = function() {

    for (var icon_name in svgIcons) {
        //store that this call has started
        exectuing = executing + 1;
        if (svgIcons.hasOwnProperty(icon_name)) {
            var url=PHP.plugin_url+'/includes/icons/'+icon_name+'.svg';
            console.log('this will run as you were expecting'); 

            //this ajax call is then started and moves to next iteration
            jQuery.get(url, function(data) {
                //This is run after the ajax call has returned a response, not in the order of the code
                svgIcons[icon_name]=data;

                console.log('iterating');
                console.log(svgIcons[icon_name]); //outputs svg
                //if you want to call a function after evey call is comeplete then ignore the 'executing' part and just call the function here.
                //decrement value as this call has finished
                executing = executing - 1;

                //if all have finished then call the function we want
                if(executing === 0){
                    executeAfter();
                }
            });
        }
    }

console.log('this should be after iteration');
console.log(svgIcons["arrow_left"]); //empty

}

this.executeAfter(){
    //This will be exectued after all of you ajax calls are complete.
}
this.getIcons(); //called at object initialization