我对Node.js体系结构的内部结构有了更深入的了解,我经常看到的一个术语是“蜱”,如“事件循环的下一个滴答”或函数nextTick()。
我没有看到的是一个确切的定义“滴答”是什么。基于不同的文章(就像这个),我能够把一个概念拼凑在我的脑海中,但我不知道它有多准确。
我能得到Node.js事件循环滴答的精确而详细的描述吗?
发布于 2013-11-06 21:52:02
请记住,虽然JavaScript是单线程的,但节点的所有I/O和对本机API的调用要么是异步的(使用特定于平台的机制),要么是在单独的线程上运行。(这一切都是通过libuv处理的。)
因此,当套接字上可用的数据或本地API函数返回时,我们需要一种同步的方法来调用对刚刚发生的特定事件感兴趣的JavaScript函数。
从发生本机事件的线程中调用JS函数是不安全的,其原因与在常规多线程应用程序竞争条件、非原子内存访问等中遇到的原因相同。
因此,我们所做的就是以线程安全的方式将事件放在队列中。在过于简化的psuedocode中,类似于:
lock (queue) {
    queue.push(event);
}然后,回到主JavaScript线程(但在C端),我们执行如下操作:
while (true) {
    // this is the beginning of a tick
    lock (queue) {
        var tickEvents = copy(queue); // copy the current queue items into thread-local memory
        queue.empty(); // ..and empty out the shared queue
    }
    for (var i = 0; i < tickEvents.length; i++) {
        InvokeJSFunction(tickEvents[i]);
    }
    // this the end of the tick
}while (true) (实际上并不存在于节点的源代码中;这纯粹是说明性的)表示事件循环。内部for为队列中的每个事件调用JS函数。
这是一个滴答:与任何外部事件关联的零或多个回调函数的同步调用。一旦队列被清空并返回最后一个函数,滴答就结束了。我们回到开头(下一个勾号),检查在运行JavaScript时从其他线程添加到队列中的事件。
什么东西可以添加到队列中?
process.nextTicksetTimeout/setIntervalfs、net等的东西)crypto的处理器密集型函数,如密码流、pbkdf2和PRNG (它们实际上是.的一个例子)发布于 2018-02-18 01:33:26
对于那些刚加入JavaScript的人来说,一个简单的答案是:
首先要理解的是,JavaScript是一个“单线程环境”。这指的是JavaScript在单个线程上一次从“事件循环”执行代码块的行为。下面是从凯尔辛普森的书“ydkJS”和“事后”中摘录的事件循环的基本含义,并给出一个解释:
// `eventLoop` is an array that acts as a queue (first-in, first-out)
var eventLoop = [ ];
var event;
// keep going "forever"
while (true) {
    // perform a "tick"
    if (eventLoop.length > 0) {
        // get the next event in the queue
        event = eventLoop.shift();
        // now, execute the next event
        try {
            event();
        }
        catch (err) {
            reportError(err);
        }
    }
}第一个while循环模拟事件循环。滴答是指事件从“事件循环队列”中脱队列和执行所述事件.。
请参阅“Josh3796”的响应,以更详细地解释事件的脱队列和执行过程中所发生的事情。
此外,我建议那些有兴趣深入了解JavaScript的人阅读凯尔辛普森的书。它是完全免费和开放源码的,可以在以下链接中找到:https://github.com/getify/You-Dont-Know-JS
我所引用的特定部分可以在这里找到:https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/sync-async/ch1.md
发布于 2022-06-15 14:23:02
                          ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐             
                              THE EVENT LOOP                  
                          └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘             
                                                              
                     ┌───────────────────────────────┐        
                     │             poll              │        
                  ┌─▶│                               │──┐     
                  │  └───────────────┬───────────────┘  │     
                  │                  │                 tick   
                  │  ┌───────────────▼───────────────┐  │     
                  │  │             check             │  │     
                  │  │                               │◀─┘     
                  │  └───────────────┬───────────────┘        
                  │                  │                        
                  │  ┌───────────────▼───────────────┐        
                  │  │        close callbacks        │        
                  │  │                               │        
                loop └───────────────┬───────────────┘        
                  │                  │                        
                  │  ┌───────────────▼───────────────┐        
                  │  │            timers             │        
                  │  │                               │        
                  │  └───────────────┬───────────────┘        
                  │                  │                        
                  │  ┌───────────────▼───────────────┐        
                  │  │       pending callbacks       │        
                  │  │                               │        
                  │  └───────────────┬───────────────┘        
                  │                  │                        
                  │  ┌───────────────▼───────────────┐        
                  │  │         idle, prepare         │        
                  └──│                               │        
                     └───────────────────────────────┘        事件循环(在Node.js中)是一个执行模型,在该模型中,脚本的各个方面按照定义的时间表以周期性的方式执行。
It事件循环由多个阶段组成(如上文所示)。每个阶段包含(1)一个调用堆栈,(2)一个回调队列。调用堆栈是执行代码的地方(在LIFO的基础上),而回调队列是代码被调度的位置(在FIFO的基础上),以便稍后放置在调用堆栈中以供执行。
这个回调队列可以细分为两个队列:一个microTask队列和一个macroTask队列。微任务(一旦调度)是在当前阶段中当前运行脚本之后立即执行的任务,而宏任务(一旦调度)则是在所述阶段的下一个循环中执行的任务(在该阶段中的任何微任务之后)。
事件循环在一个循环中反复运行,直到没有更多的工作要做。每个循环(贯穿所有阶段)都可以称为循环,而在给定队列中对脚本的每次完整调用都可以称为滴答。
这个滴答通常会从一个阶段发生到另一个阶段,但是当microTask队列和macroTask队列都不是空时(例如,在运行脚本中解析承诺时),它的then方法会将项添加到microTask队列中。
当您编写代码(例如在mycode.js文件中)并调用它(使用node mycode.js)时,将根据其编写方式使用事件循环执行此代码。
下面是一个示例脚本:
process.nextTick(function() {
  console.log('next tick - 1 [scheduled from poll]');
});
console.log('poll phase - 1');
setImmediate(function() {
  console.log('check phase - 1');
  process.nextTick(function() {
    console.log('next tick - 2 [scheduled from check]');
  });
  Promise.resolve()
    .then(function() {
      console.log(`check phase - 1.1 [microTask]`);
    })
    .then(function() {
      console.log(`check phase - 1.2 [microTask]`);
    })
    .then(function() {
      setTimeout(function() {
        console.log('timers phase [scheduled from Promise in check]');
      });
      process.nextTick(function() {
        console.log('next tick - 3 [scheduled from Promise in check]');
      });
    });
  console.log('check phase - 2');
});
setTimeout(function() {
  console.log('timers phase - 1');
  setImmediate(function() {
    console.log('check phase [scheduled from timers]');
  });
  Promise.resolve()
    .then(function() {
      console.log('timers phase - 1.1 [microTask]');
    })
    .then(function() {
      console.log('timers phase - 1.2 [microTask]');
    })
    .then(function() {
      setTimeout(function() {
        console.log('timers phase [scheduled from Promise in timers]');
      });
    });
});
process.nextTick(function() {
  console.log('next tick - 4 [scheduled from poll]');
});
console.log('poll phase - 2');将此文件复制(或键入)到.js文件中,并使用node调用它。
您应该得到以下输出:
poll phase - 1
poll phase - 2
next tick - 1 [scheduled from poll]
next tick - 4 [scheduled from poll]
check phase - 1
check phase - 2
next tick - 2 [scheduled from check]
check phase - 1.1 [microTask]
check phase - 1.2 [microTask]
next tick - 3 [scheduled from Promise in check]
timers phase - 1
timers phase - 1.1 [microTask]
timers phase - 1.2 [microTask]
timers phase [scheduled from Promise in check]
check phase [scheduled from timers]
timers phase [scheduled from Promise in timers]注意事项:使用Node.js版本16.15.0
在解释之前,以下是一些需要记住的规则:
setImmediate计划脚本在事件循环的下一个检查阶段运行(在macroTask队列中)setTimeout计划脚本在事件循环的下一个定时器阶段运行(在macroTask队列中)Process.nextTick计划脚本在下一个刻度之前运行,即(1)在当前脚本运行之后,但在microTask队列运行之前,或者(2)在事件循环之前运行,如果microTask队列为空,则从一个阶段遍历到下一个阶段。Promise.prototype.then将脚本安排在当前microTask队列中运行,即在当前脚本之后,但在计划到下一阶段的脚本之前。以下是以事件时间表的形式作出的解释:
A.来自投票阶段(循环1)
console.log('poll phase - 1')和console.log('poll phase - 2')是同步代码,将在当前阶段立即运行。console.log('next tick - 1 [scheduled from poll]')和console.log('next tick - 4 [scheduled from poll]')由process.nextTick计划在下一个滴答之前运行,即在检查阶段之前运行(因为microTask队列中没有任何内容)。setImmediate上的回调(第7行)计划在检查阶段运行setTimeout上的回调(第33行)计划在计时器阶段运行在检查阶段(循环1)之前5.执行console.log('next tick - 1 [scheduled from poll]')和console.log('next tick - 4 [scheduled from poll]')
从检查阶段(循环1) 6. console.log('check phase - 1')和console.log('check phase - 2')从setImmediate以前计划的回调(第7行)立即执行,因为它们是同步的7。console.log('next tick - 2 [scheduled from check]')由process.nextTick 8调度。第15、18和21行上的回调计划在microTask队列中运行。9.执行console.log('next tick - 2 [scheduled from check]') (因为这是在下一个滴答之前,即当前脚本之后,但在microTask队列之前) 10。执行第15行和第18行的回调(因为在运行脚本之后立即执行microTask ) 11。执行第21行的回调,并安排(1)在下一个定时器阶段运行console.log('timers phase [scheduled from Promise in check]'),(2)在下一个滴答之前运行console.log('next tick - 3 [scheduled from Promise in check]'),即在从当前阶段(检查)遍历到下一个活动阶段(定时器)之前运行。
在定时器阶段(循环1)之前12.执行console.log('next tick - 3 [scheduled from Promise in check]')
从定时器阶段(循环1) 13.执行console.log('timers phase - 1') 14。setImmediate (第36行)安排在下一个检查阶段15中运行回调。Promise (第40行)调度在microTask队列16中运行三个回调。console.log('timers phase - 1.1 [microTask]')和console.log('timers phase - 1.2 [microTask]')按计划在15.17中执行。console.log('timers phase [scheduled from Promise in check]')被执行。它以前是由setTimeout计划的(第22行)。它现在正在运行(在上面的代码之后),因为它是一个macroTask (所以它在运行microTask队列之后运行)
从下一个检查阶段(循环2)开始执行18. console.log('check phase [scheduled from timers]')。它以前被setImmediate (第36行)安排在计时器阶段(循环1)。
从下一次定时器阶段(循环2) 19. console.log('timers phase [scheduled from Promise in timers]')被执行。它以前被setTimeout (第48行)安排在计时器阶段(循环1)。
参考资料
https://stackoverflow.com/questions/19822668
复制相似问题