Javascript - 在异步函数调用上处理竞争条件

时间:2015-04-27 13:56:14

标签: javascript ajax asynchronous

本周末,我在Javascript中遇到了处理竞争条件的特殊问题。

以下是给我问题的代码:



function myFunction(requestObj, myArray) {
	for(var i=0;i<myArray.length;i++) {
		//AJAX call
		makeAjaxCall(requestObj, function(data) {
		  //Callback for the ajax call
		  //PROBLEM : HOW CAN I ACCESS VALUE OF 'i' here for the iteration for which the AJAX call was made!!!
		}
	}
}
&#13;
&#13;
&#13;

获取&#39; i&#39;的价值在AJAX回调中不会给我预期的值,因为当AJAX呼叫响应回来时,&#39; for&#39;循环会经过多次迭代。

为了解决这个问题,我采用了以下方法:

&#13;
&#13;
function myFunction(requestObj, myArray) {
	var i = 0;

	function outerFunction() {
		makeAjaxCall(requestObj, innerFunction);
	}

	function innerFunction(data) {
	  i++;
	  if(i<myArray.length) {
	  	outerFunction();
	  }
	}

	outerFunction();
}
&#13;
&#13;
&#13;

这是正确的做法吗?假设它是我无法修改的第三方AJAX库调用,我可以通过其他方式改进这一点。

3 个答案:

答案 0 :(得分:4)

你只需要使用一个闭包:

function myFunction(requestObj, myArray) {
    for(var i=0;i<myArray.length;i++) {
        //AJAX call closed over i
        (function(i) { // wrap your call in an anonymous function
          makeAjaxCall(requestObj, function(data) {
            // i is what you think it is
          }
        })(i) // pass i to the anonymous function and invoke immediately
    }
}

答案 1 :(得分:0)

问题是,您传递给ajax调用的回调有一个持久引用i,而不是创建它时的值的副本。

你的方法很好除了它等待第二次ajax调用直到第一次完成,然后等待第二次完成才能完成第三次,等等。除非你拥有

有两种选择:

  1. 使用构建器功能:

    function myFunction(requestObj, myArray) {
        for(var i=0;i<myArray.length;i++) {
            //AJAX call
            makeAjaxCall(requestObj, buildHandler(i));
        }
        function buildHandler(index) {
            return function(data) {
                // Use `index` here
            };
        }
    }
    

    现在,处理程序引用index,而不是i,而不是Function#bind

  2. 使用function myFunction(requestObj, myArray) { for(var i=0;i<myArray.length;i++) { //AJAX call makeAjaxCall(requestObj, function(index, data) { // Use index here }.bind(null, i)); } }

    Function#bind

    this创建一个函数,在调用时,将调用具有特定bind值的原始函数(我们没有使用上面的值)和您传递的任何参数using (PowerShell PowerShellInstance = PowerShell.Create()) { PowerShellInstance.AddScript(@"cd (Join-Path (Join-Path $env:HOMEDRIVE $env:HOMEPATH) desktop); $null >> newfile.txt"); PowerShellInstance.Invoke(); } - 然后是赋予绑定函数的任何参数。

  3. 我更喜欢#1:读取很清楚并且没有创建一堆不必要的函数(理论上,#2每个循环创建两个函数而不是一个)。

答案 2 :(得分:0)

还有另外两种方法:使用bind,使用闭包

使用bind

function myFunction(requestObj, myArray) {
    for(var i=0;i<myArray.length;i++) {
        makeAjaxCall(requestObj, function(idx,data) {
          //idx will be correct value
        }.bind(null,i));
    }
}

使用闭包:

function myFunction(requestObj, myArray) {
    for(var i=0;i<myArray.length;i++) {
        (function(idx){
           makeAjaxCall(requestObj, function(data) {
             //idx will be correct value 
           });
        })(i);
    }
}

还有第三种方法,使用另一个函数来创建回调

function myFunction(requestObj, myArray) {
    function makeCB(idx){
       return function(){
          //do stuff here
       }
    }
    for(var i=0;i<myArray.length;i++) {
        makeAjaxCall(requestObj, makeCB(i));
    }
}