异步性是a)函数或b)代码行的常量?

时间:2017-06-23 18:47:40

标签: javascript arrays node.js asynchronous

join(user) {
    if(this.room.length === 2) throw new Error('The room is full');
    this.room.push(user);
};

如果异步性发挥作用'基础,这个代码是100%防弹。一个房间里不可能有两个以上的用户。

如果异步发生在每条线上'基础,此代码可能会失败。为什么?因为如果三个用户同时进入房间,则可能在10毫秒间隔内发生以下情况:

1ms:房间是空的。让用户A进入,推送到阵列。

4ms:房间有一个用户。

5ms:用户B要求加入。

6ms:用户C要求加入。

7ms:检查数组的长度(1)。

8ms:检查数组的长度(1)。

9ms:将用户B推送到阵列,因为房间长度为1。

10ms:将用户C推送到阵列,因为房间长度为1。

15ms:现在房间有3个用户,最多只有2个。

如果异步发生在每条线上'基础,我如何避免前一个例子在真实场景中发生?对不起,如果我没有按照他们的名字打电话,但我想不出更好的解释方法。

评论建议(但不能肯定地并且明确地说)“'实例'相同的功能不会相互重叠。那些指向同一个对象/数组的不同函数呢?

join(user) {
    if(GLOBAL.room1.length === 2) throw new Error('The room is full');
    GLOBAL.room1.push(user);
};

join2(user) {
    if(GLOBAL.room1.length === 2) throw new Error('The room is full');
    GLOBAL.room1.push(user);
};

1 个答案:

答案 0 :(得分:1)

Javascript是每个事件同步的(比函数或行更大的范围)。这排除了您自己开始的任何异步操作。他们将开始,然后在他们自己的事件后完成一段时间(请参阅后面的事件讨论)。事件的同步代码(如您显示的内容)将在下一个事件运行之前完全运行完毕。

您在nodejs中的javascript以单线程和事件驱动的方式运行。这意味着一个Javascript开始执行,它将在任何其他事件被处理之前完成。因此,一个函数在任何人再次调用它之前运行并完成(假设它没有自己调用),因此你没有可以存在于多线程环境中的典型竞争条件类型。

  

如果异步性发挥作用'基础,这个代码是100%防弹。一个房间里不可能有两个以上的用户。

node.js中的Javascript是单线程的,它将运行整个事件处理程序以完成(该事件处理程序中的所有函数),然后再运行下一个事件。

  

如果异步发生在每条线上'基础,此代码可能会失败。为什么?因为如果三个用户同时进入房间,则可能在10毫秒间隔内发生以下情况:

Javascript的单线程不是每行或每个功能。它是每个事件。因此,在这种情况下,您似乎可能正在处理传入的socket.io消息。这意味着该传入消息事件处理程序中的所有代码将在任何其他传入消息可以处理之前运行。只有当您的事件处理程序将控制权返回给系统时(通过从其事件处理函数返回),nodejs才会获取事件队列中的下一个事件并调用其事件处理程序。

所以,让我们说三个用户都要求大约在同一时间加入同一个房间并检查这个场景,以进一步解释这是如何工作的。

  1. 所有三位客户都要求大约在同一时间加入同一个房间。
  2. 其中一个请求比另一个请求更快到达(传入的数据包在网络电缆和传入的TCP堆栈中序列化,因此其中一个首先到达)。
  3. 该数据包由本地TCP堆栈处理,然后提醒nodejs中的侦听套接字(这将是本机代码,并在与JS引擎不同的线程中运行)。
  4. nodejs平台代码获取此套接字通知,并将事件插入nodejs事件队列,以警告nodejs代码侦听该传入消息。
  5. 如果nodejs Javascript引擎此刻没有做任何事情,那么它会立即触发该事件处理程序并开始运行与该事件处理程序关联的代码。
  6. 如果nodejs Javascipt引擎正在执行某些操作,那么事件将位于事件队列中,直到JS引擎完成当前正在执行的操作,然后它将获取事件队列中的下一个事件。
  7. 现在另外两个也加入同一个房间的请求到达TCP堆栈。
  8. 服务于传入TCP数据包的nodejs中的本机代码,请参阅这两个请求中的每一个到达。它们被插入到nodejs事件队列中,就像第一个一样。因为JS解释器忙于为到​​达的第一条消息提供服务,所以这些消息现在就位于事件队列中。
  9. 当第一条消息完成其处理时,nodejs解释器从事件队列中获取下一个事件(第二个到达的消息现在将被处理)。将调用其事件处理程序,并在处理第三个事件之前运行完成。
  10. 希望通过对事物处理方式的描述,您可以看到您对房间长度的检查没有任何可能的竞争条件。将用户添加到房间是单线程的,因此它可以在任何其他加入房间的请求被处理之前启动,进行和完成。这种单线程特性(虽然偶尔是一个限制特性)极大地简化了nodejs编程,因为它消除了多线程编程所具有的竞争条件的许多可能原因。

    让我们通过对每一步的评论来查看您的序列:

      

    1ms:房间是空的。让用户A进入,推送到阵列。

         

    4ms:房间有一个用户。

         

    5ms:用户B要求加入。

    如果第一个请求尚未处理,则此请求将被放入事件队列,直到第一个请求完成。如果第一个请求完成,则该请求将开始处理。

      

    6ms:用户C要求加入。

    假设第二个请求需要超过1毫秒才能处理,所以它还没有完成,这个请求将进入事件队列,并且在完成前两个请求之前不会被处理。

      

    7ms:检查数组的长度(1)。

    我不明白这一步应该是什么。如果这是处理第二个请求的一部分,那么第二个和第三个请求仍然在事件队列中,并且在第一个请求完成之前无法运行。

      

    8ms:检查数组的长度(1)。

    与之前的评论相同。

      

    9ms:将用户B推送到阵列,因为房间长度为1。

         

    10ms:将用户C推送到阵列,因为房间长度为1。

         

    15ms:现在房间有3个用户,最多只有2个。

    这是有缺陷的逻辑。由于事件队列和nodejs Javascript的单线程特性,userB和userC请求在userA请求完成之前不会运行。因此,当任何请求运行时,房间长度始终是准确的。像这样的多个请求不会同时运行。