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

标签 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进入数组。

4毫秒:会议室中只有一位用户。

5毫秒:用户B要求加入。

6毫秒:用户C要求加入。

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

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

9ms:将用户B推入数组,因为房间长度为1。

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

15毫秒:现在会议室有3个用户,最多两个。

如果异步性是基于“每行”的,那么如何避免前面的示例在实际场景中发生?抱歉,如果我没有用事物的名字来称呼事物,但是我想不出更好的方法来解释这一点。

这些评论暗示(但不能确定并以明确的方式说)相同功能的“实例”永远不会重叠。指向相同对象/数组的不同函数呢?

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);
};

最佳答案

Javascript是每个事件同步的(范围大于函数或行)。这不包括您自己启动的任何异步操作。他们将开始,然后在一段时间后以自己的事件结束(请参见此处稍后的事件讨论)。事件的同步代码(如您显示的内容)将完全运行到下一个事件可以运行之前。

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


  如果异步基于“功能”进行,则此代码是100%防弹的。一个房间中不可能有两个以上的用户。


node.js中的Javascript是单线程的,它将在运行下一个事件之前运行整个事件处理程序以完成操作(该事件处理程序中的所有功能)。


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


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

因此,假设三个用户都要求大约同时进入同一房间,并检查该场景以进一步说明其工作原理。


所有三个客户都要求大约同时进入同一房间。
这些请求中的一个比另一个请求稍早到达(传入的数据包在网络电缆和传入的TCP堆栈中进行序列化,因此其中一个首先到达)。
该数据包由本地TCP堆栈处理,然后向nodejs中的侦听套接字发出警报(这将是本机代码,并在与JS引擎不同的线程中运行)。
nodejs平台代码获取此套接字通知,并将事件插入到nodejs事件队列中,以警告nodejs代码侦听该传入消息。
如果nodejs Javascript引擎当前未执行任何操作,则它将立即触发该事件处理程序并开始运行与该事件处理程序关联的代码。
如果nodejs Javascipt引擎当前正在执行某项操作,则该事件将位于事件队列中,直到JS引擎完成其当前正在执行的操作,然后再获取事件队列中的下一个事件。
现在,另外两个请求也加入同一个房间,到达TCP堆栈。
服务于传入TCP数据包的nodejs中的本机代码,请参阅这两个请求中的每一个。就像第一个一样,它们被插入到nodejs事件队列中。由于JS解释器正忙于处理到达的第一条消息,因此这些消息现在仅位于事件队列中。
当第一个消息完成处理后,nodejs解释器将从事件队列中获取下一个事件(第二个到达的消息将立即得到处理)。它的事件处理程序将被调用,并将在处理第三个事件之前运行完成。


希望从对事物处理方式的描述中可以看到,检查房间长度没有任何可能的竞争条件。将用户添加到会议室是单线程的,因此它将在处理任何其他加入会议室的请求之前开始,进行和完成。这种单线程性质(有时是一个限制功能)极大地简化了nodejs编程,因为它消除了多线程编程所具有的许多竞争条件的可能原因。



让我们用每个步骤的注释来看看您的顺序:


  1ms:房间是空的。让用户A进入数组。
  
  4毫秒:会议室中只有一位用户。
  
  5毫秒:用户B要求加入。


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


  6毫秒:用户C要求加入。


假设第二个请求要花费超过1毫秒的时间来处理,因此尚未完成,那么该请求将进入事件队列,并且直到前两个请求都完成后才会被处理。


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


我不知道此步骤的含义。如果这是处理第二个请求的一部分,则第二个和第三个请求仍在事件队列中,并且在第一个请求完成之前无法运行。


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


与之前的评论相同。


  9ms:将用户B推入数组,因为房间长度为1。
  
  10ms:将用户C推送到数组,因为房间长度为1。
  
  15毫秒:现在会议室有3个用户,最多两个。


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

关于javascript - a)函数或b)代码行的异步性是常数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44728125/

相关文章:

javascript - Kinetic js保存 Canvas 阶段

javascript - 在 JavaScript 中,将 NodeList 转换为数组的最佳方法是什么?

javascript - Orchestrate.js 同时触发 'promise.fail' 和 'promise.then'

javascript - 在服务器端实现 Paper.js 螺旋光栅示例

C - 删除字符串中特定字符周围的空格

node.js - 类型 'push' 中缺少属性 '(request: NtlmRequest, response: Response) => void'

javascript - 如何在 REACT js 中正确从 JSON 对象中删除特定项目?

javascript - React 教程和 Sinatra API : Uncaught TypeError: this. props.data.map 不是函数

javascript - 为什么使用 location.reload() 时我的页面没有刷新?

arrays - Word VBA - 循环通过 "AND"删除书签