如何确保SAME JavaScript函数不会被单独的事件同时调用?

时间:2018-09-07 08:15:22

标签: javascript html

我有一个用JS和socket.io编写的多用户聊天网页。

许多用户可以同时发送消息,当我收到一条消息时,我需要运行一个函数来更改DIV的innerHTML。

但是,假设要同时发送1000条消息,我想依次运行THAT函数。

即除非最后一次调用完成,否则不要对该函数运行另一个调用。 (并且该函数内的GLOBAL变量将不会被另一个SAME函数调用更改,直到当前调用完成为止)

例如(我通过使用计时器而不是socket.io事件来编写以下示例,因为它很容易理解)

下面是Javascript:

var gInteger = 0;
var gbolChanging = false;

function changeInteger (intChange) {
  if (!gbolChanging) {
    gbolChanging = true;
	  gInteger += intChange;
    divInteger.innerHTML = gInteger;
    gbolChanging = false;
  }
}

var timer1 = setInterval(changeInteger, 1, 1);
var timer2 = setInterval(changeInteger, 1, -1);
<div id="divInteger">

</div>

上面是HTML:

上述html和js的预期结果是:

您应该只能看到0或1

实际结果是:

如果运行足够长的时间,您可能还会看到-1和2。 (即两个计时器都同时检测到bolChanging = false)

2 个答案:

答案 0 :(得分:4)

Javascript不是多线程的,因此不能通过单独的事件同时调用同一函数。更一般而言,相同的变量不能同时由不同的指令访问(在线程环境中,两个线程访问的变量可能会在内存中重复,并且会发生同步问题)。

作为证明,请在事件处理程序中进行长时间的同步操作,以确保您的界面将冻结直到结束(并且无法处理其他事件)。

获得的结果不是同时调用该函数的结果,而是调用顺序是不可预测的的结果。

现在,您仍然可以通过使用某种信号量来确保顺序得到遵守:

var gInteger = 0;
var gbolWaitingForPlusOne = true;
var gbolWaitingForMinusOne = false;
var divInteger = document.getElementById('divInteger');

function changeInteger(intChange) {
    if (
        (gbolWaitingForPlusOne && intChange !== 1) ||
        (gbolWaitingForMinusOne && intChange !== -1)
    ) {
        return;
    }
    gbolWaitingForPlusOne = !gbolWaitingForPlusOne;
    gbolWaitingForMinusOne = !gbolWaitingForMinusOne;
    gInteger += intChange;
    divInteger.innerHTML = gInteger;
    // if you don't log you wont see because it's too fast
    console.log(gInteger);
}

var timer1 = setInterval(changeInteger, 1, 1);
var timer2 = setInterval(changeInteger, 1, -1);
<div id="divInteger">0</div>

这仍然无法解决您的问题,因为这样消息会丢失。

您将不需要根据操作类型(+ 1 / -1)而是通过某种时间戳来进行累积和重新排序。在消息传递系统中,您将按消息的创建时间而不是它们的处理时间对消息进行重新排序。如果在同一时间(很可能)同时创建了两个或更多消息,则您将必须选择自己的显示顺序,但是没有充分的理由使同一创建的消息先于另一个出现日期(这实际上是javascript以“错误”顺序拨打电话时执行的操作)

答案 1 :(得分:0)

首先,谢谢remix23,我想我找到了对调用进行排序的答案,而不会丢失Array的调用:

var gintSumA = 0;
var gintSumB = 0;
var gintSumC = 0;
var gintSumAll = 0;
var gintToBeProcess = 0;
var timerWork, timerA, timerB, timerC;
var gintMax = 1000;
var gbolWorking = false;
var garrList = [];

function funStartAdd(strCaller, intNumber) {
  switch (strCaller) {
    case "A":
      if (gintSumA < gintMax) {
        garrList.push(intNumber);
        gintSumA += intNumber;
        gintToBeProcess += 1;
      } else {
        clearInterval(timerA);
      }
      break;
    case "B":
      if (gintSumB < gintMax) {
        garrList.push(intNumber);
        gintSumB += intNumber;
        gintToBeProcess += 1;
      } else {
        clearInterval(timerB);
      }
      break;
    case "C":
      if (gintSumC < gintMax) {
        garrList.push(intNumber);
        gintSumC += intNumber;
        gintToBeProcess += 1;
      } else {
        clearInterval(timerC);
      }
      break;
    default:
      // Do Nothing
  }
}
function funActualAdd(intNumber) {
  gintToBeProcess -= 1;
  gintSumAll += intNumber;
}
function funWork() {
  if (!gbolWorking) {
    gbolWorking = true;
    var intTemp = garrList[0];
    garrList.splice(0,1);
    funActualAdd(intTemp);
    
    if (gintSumA == gintMax &&
     gintSumB == gintMax &&
     gintSumC == gintMax &&
     gintToBeProcess == 0) {
    // Display Result
    divResult.innerHTML = "SumA = " + gintSumA + "<br/>" +
                   "SumB = " + gintSumB + "<br/>" +
                   "SumC = " + gintSumC + "<br/>" +
                   "SumAll = " + gintSumAll;
    clearInterval(funWork);
    }
    gbolWorking = false;
  }
}

timerA = setInterval(funStartAdd, 1, "A", 1);
timerB = setInterval(funStartAdd, 1, "B", 1);
timerC = setInterval(funStartAdd, 1, "C", 1);

timerWork = setInterval(funWork, 1);
<div id="divResult">

</div>