JavaScript回调函数过早执行

3

大家好,我有以下循环,其中我调用另一个模块的vehicles.alertsVid函数,该函数返回每个车辆的状态。结果确实到达了,但是较晚,并且没有被正确添加。

请问应该如何编写代码,以便主循环不会超前,并且及时写入temp.status? :)

for (var i = vehiclesStuff.length - 1; i >= 0; i--) {
var temp = {};
temp.name = vehiclesStuff[i].nickname.S;
temp.plate = vehiclesStuff[i].plate.S;
temp.base = vehiclesStuff[i].base.S;
temp.vin = vehiclesStuff[i].vin.S;
temp.vid = vehiclesStuff[i].vid.S;

var myfunc = function(t,results,done){return function(){
    console.log ("ALERTS",results);
    t.status = results.toString();
    done();
}};

vehicles.alertsVid(temp.vid, myfunc(temp));

vehicleArray.push(temp);
};
callback()

1
你漏掉了所有的分号,还把一个放到不该放的地方了吗? - elclanrs
@elclanrs - 啊,一个辩论分号的机会。虽然它们不在那里,但我实际上并不“想念”它们...(不,我真的不想辩论它。) - nnnnnn
尽管代码看起来是这样,但由于提升的原因,temp并不是循环作用域的变量。因此,如果传递给alertsVid的函数实际上在稍后被调用,它将使用temp的最终值来调用所有循环版本,而不是逐个遍历可能性。这就是正在发生的吗? - kybernetikos
2
因此,主for循环不会超前。请注意,这不是“竞赛”情况。您的JS将是单线程的,但是假定vehicles.alertsVid()触发了一个异步过程,因此您传递给它的函数在整个for循环完成之后以及callback()返回之后才会被调用。@elclanrs - 是的,我知道。我只是在开玩笑(如果你能想象得到)。 - nnnnnn
1
好的,希望我的第二个答案有所帮助。 - Arjun Mehta
显示剩余3条评论
3个回答

2
for (var i = vehiclesStuff.length - 1; i >= 0; i--) {
    var temp = {}
    temp.name = vehiclesStuff[i].nickname.S;
    temp.plate = vehiclesStuff[i].plate.S;

    var myfunc=function(t,results,done){return function(){
        console.log ("ALERTS",results);
        t.status = results.toString();
        done();
    }};


    vehicles.alertsVid(temp.vid, myfunc(temp,results, done));

    vehicleArray.push(temp);
};

当您调用内部函数时,变量将绑定到调用堆栈中在调用时的状态。因此,回调函数不会“超前”。

请注意,该函数本身返回一个函数,因此有双重嵌套。这是我的签名之一。=)


2
我认为问题不在于结果/完成,而是在于temp。实际上,我更怀疑回调函数应该接收results/done作为参数,因此你的代码可能是颠倒了,返回的函数仍然应该将results/done作为参数,但外部函数应该将temp作为参数。 - kybernetikos
@Schien:无论如何,你仍然将undefined传递给results,而不是从回调参数中获取它们。 - Bergi
伙计们,如果我用你们的建议替换我的代码,会有类似的行为: http://pastie.org/8649335 - Cmag
我已经更新了我的代码,有什么建议吗? - Cmag
1
这个方法也需要你能够修改vehicles.alertsVid,以便接收整个temp对象并将其作为t返回。如果不修改vehicles.alertsVid以传递temp对象,则无法正常工作。 - Arjun Mehta
显示剩余5条评论

2
你应该熟悉闭包的概念。闭包是一种强大的方式,可以捕获任何给定评估/执行链的上下文。
function callbackClosure(temp){
  return function(results, done){
    console.log ("ALERTS",results);
    temp.status = results.toString();
    done();
  };
}

for (var i = vehiclesStuff.length - 1; i >= 0; i--) {
  var temp = {};
  temp.name = vehiclesStuff[i].nickname.S;
  temp.plate = vehiclesStuff[i].plate.S;
  temp.base = vehiclesStuff[i].base.S;
  temp.vin = vehiclesStuff[i].vin.S;
  temp.vid = vehiclesStuff[i].vid.S;

  vehicles.alertsVid(temp.vid, callbackClosure(temp));

  vehicleArray.push(temp);
}

这段代码的作用是,通过返回一个函数,创建了一个新的上下文,这个上下文受callbackClosure执行实例的影响,并且将传入的特定对象在其独特的范围内保留直到执行完成。
这假设你的vehicles.alertsVid在其回调中执行并返回一个results对象和一个done函数。
请访问此问题了解有关闭包的更多信息:JavaScript 闭包工作原理是什么?

很遗憾,按照您的“callbackClosure(temp)”仍然无法工作... 这是一个Gist链接:https://gist.github.com/vasiliyb/8527211 - Cmag
我觉得你还没有完全领会异步编程的概念,更不用说JavaScript了。此外,你仍然没有正确地使用分号。在你的gist中,请尝试从回调闭包函数返回的函数中执行console.log(temp)。另外,请看这些视频:http://yuiblog.com/crockford/ 除此之外,我已经尽力帮忙了。看看能否让它正常工作! ;) - Arjun Mehta

1
如果您可以控制从车辆调用的alertsVid函数,您可能希望将整个temp对象传递给它,让它处理而不是temp.vid (这个属性到底来自哪里?)
通过这种方式,您正在传递相同的对象引用,因此当最终处理它时,您正在向正确的temp (rightTemp)添加状态。这样它就可以将正确的temp作为回调的参数返回。
您正在初始化一个新的对象temp,并添加nameplate。我没有看到temp上的vid属性?
另外,如果您想要等待所有temp的状态都被处理并且在调用最终回调之前,请使用计数器等待其触发。如果您不想这样做,请忽略那些行... (我已经用"// callBack handling"进行了注释)
还有...修复那些分号。 :P
var numProcessed = 0; // callBack handling
var numToProcess = vehiclesStuff.length; // callBack handling

for (var i = vehiclesStuff.length - 1; i >= 0; i--) {
    var temp = {};
    temp.name = vehiclesStuff[i].nickname.S;
    temp.plate = vehiclesStuff[i].plate.S;

    vehicles.alertsVid(temp, function (rightTemp, results, done) {
        console.log ("ALERTS",results);
        rightTemp.status = results.toString();            
        done();
        processedTemp(); // callBack handling
    })

    vehicleArray.push(temp);
}

// callBack handling
function processedTemp(){
  numProcessed++;
  if(numProcessed === numToProcess) callBack();
}

你的 vehicles.alertsVid 函数将会是这样:

vehicles.alertsVid = function(temp, callBack){
  ...
  callBack(temp, results, done); // using the same temp (object reference) that was passed in!!!
};

这一切都建立在您对vehicles.alertsVid函数有控制的基础上。

谢谢您先生。如果我无法控制alertsVid函数怎么办? - Cmag
我已经更新了我的代码,有什么建议吗? - Cmag
temp.vid = vehiclesStuff[i].vid.S;... 在原始粘贴中忘记包含了。 - Cmag
请参考Quentin对您原始帖子的评论! - Arjun Mehta
添加了第二个答案。 - Arjun Mehta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接