精华内容
下载资源
问答
  • Node的事件循环

    2019-10-02 07:23:17
    Node的事件循环: 进程启动时,node便会创建一个类似于while(true)的循环,每执行一次循环体我们称为tick,每次tick的过程都会去查询是否有事件待处理,如果有就取出事件及其相关的回调函数,如果存在回调函数,就...

    Node的事件循环:
            进程启动时,node便会创建一个类似于while(true)的循环,每执行一次循环体我们称为tick,每次tick的过程都会去查询是否有事件待处理,如果有就取出事件及其相关的回调函数,如果存在回调函数,就只执行它们,然后进入下个循环。
    Node的事件循环存在不同阶段
            timers阶段(setTimeout和setInterval的callback)
            I/O阶段:文件请求,TCP请求的callback,也就是除了 close事件的callbacks、被timers(定时器,setTimeout、setInterval等)设定的callbacks、setImmediate()设定的callbacks之外的callbacks;
            idle,prepare阶段:node内部使用
            poll阶段:获取新的I/O事件,适当的条件下node将阻塞在这里
            check阶段:执行setImmediate设定的callbacks
            close阶段:比如socket.on(‘close’,callback)设置的callback会在这个阶段执行
     
            poll阶段:
                poll阶段是衔接整个event loop各个阶段比较重要的阶段,这个阶段,除timer,close,setImmediate之外的所有异步方法完成时,都会将callback加到poll queue里并立即执行。
                两个主要的功能:1⃣️执行poll queque的callback2⃣️当timer设置的时间到达时,循环到timer阶段执行对应的callback
                1.如果事件循环到poll阶段,且代码未设置了timer:
                    如果poll queue不为空,将同步的执行queue里面的callback,直至queue为空,或执行callback达到系统上限
                    poll queue清空或者进来就为空的话,将会判断代码有没有被setImmediate设置callback,如果有则进入check阶段执行check阶段的queue,如果没有设定setImmediate callback,event loop将阻塞在这个阶段等待callback加入poll queue
                2.如果事件循环到poll阶段,且代码设置了timer:
                    清空poll queue进入空闲状态后,event loop将检查timers,如果有一个或者多个timers时间已经到达,event loop将按循环顺序进入timer阶段,并执行timer queue
            process.nextTick()
                    Process.nextTick()不属于任何阶段,而是在各个阶段切换的中间执行。
    观察者:
            判断是否有事件需要处理的过程就是询问观察者的过程,每个事件循环都会有一个或者多个观察者。
            事件循环是一个典型的生产者消费者模型,异步I/O,定时器,网络请求这些都是事件的生产者,执行完成之后,这些事件会被传递到对应的观察者那里,事件循环则从观察者里取出事件进行处理
            观察者是有优先级的,也就是说事件循环时候会按照观察者的优先级顺序去找观察者寻找事件,也就是上述所说的事件循环的六个阶段。 

    转载于:https://www.cnblogs.com/pianruijie/p/11459325.html

    展开全文
  • 宏任务:setTimeout,setInterval, 微任务:promise的回调 ...Node的事件循环比较复杂  Node的事件循环分为六个阶段 (1)timers计时器 执行setTimeout、setInterval的回调函数 (2)I/0 callba...

    宏任务:setTimeout,setInterval,

    微任务:promise的回调

    js的事件循环机制比较简单

    先执行主线程代码,执行完毕后,清空微任务队列,然后取出一个宏任务,然后清空微任务队列,如此循环

    Node的事件循环比较复杂 

    Node的事件循环分为六个阶段

    (1)timers计时器  执行setTimeoutsetInterval的回调函数

    (2)I/0 callbacks  执行I/O callback被延迟到下一阶段执行;

    (3)idle, prepare   队列的移动,仅内部使用

    (4)poll 轮询阶段 这个阶段是用来执行和 IO 操作有关的回调的,Node会向操作系统询问是否有新的 IO 事件已经触发,然后会执行响应的事件回调。几乎所有除了 定时器事件、 setImmediate() 和 close callbacks 之外操作都会在这个阶段执行。

    (5)check 这个阶段会执行 setImmediate() 设置的任务

    (6)close  执行close事件的callback,例如socket.on("close",func) 如果一个 socket 或 handle(句柄) 突然被关闭了,例如通过 socket.destroy() 关闭了,close事件将会在这个阶段发出。

    参考:https://juejin.im/post/5b5f365e6fb9a04fa8673f97

    https://juejin.im/post/5b50af02e51d45190f4ab64b

    https://cnodejs.org/topic/5a9108d78d6e16e56bb80882

    展开全文
  • node的事件循环是线程吗What makes Node.js so performant and scalable? Why is Node the technology of choice for so many companies? In this article, we will answer these questions and look at some of the...

    node的事件循环是线程吗

    What makes Node.js so performant and scalable? Why is Node the technology of choice for so many companies? In this article, we will answer these questions and look at some of the advanced concepts that make Node.js unique. We will discuss:

    是什么使Node.js如此高性能和可扩展性? 为什么Node是这么多公司的首选技术? 在本文中,我们将回答这些问题,并研究使Node.js变得独一无二的一些高级概念。 我们会讨论:

    1. Event Loops ➰

      事件循环➰
    2. Concurrency Models 🚈

      并发模型🚈
    3. Child Processes 🎛️

      子进程🎛️
    4. Threads and Worker Threads 🧵

      线程和工作线程🧵

    JavaScript developers with a deeper understanding of Node.js reportedly earn 20-30% more than their peers. If you are looking to grow your knowledge of Node.js then this blog post is for you. Let’s dive in 🤿!!

    据报道,对Node.js有更深入了解JavaScript开发人员的收入比同行高20-30%。 如果您想增加对Node.js的了解,那么此博客文章非常适合您。 让我们潜入🤿!

    运行Node.js程序会怎样? (What happens when you run a Node.js Program?)

    When we run our Node.js app it creates

    当我们运行Node.js应用程序时,它会创建

    • 1 Process 🤖

      1过程🤖
    • 1 Thread 🧵

      1个螺纹🧵
    • 1 Event Loop ➰

      1个事件循环➰

    A process is an executing program or a part of an executing program. An application can be made out of many processes. Node.js runtime, however, initiates only one process.

    一个过程 是执行程序或执行程序的一部分。 一个应用程序可以由许多过程组成。 但是,Node.js运行时仅启动一个进程。

    A thread is a basic unit to which the operating system allocates processor time. Think of threads as a unit that lets you use part of your processor.

    一个线程 是操作系统分配处理器时间的基本单位。 将线程视为一个可以使用处理器一部分的单元。

    An event loop is a continuously running loop (just like a while loop). It executes one command at a time, more on this later. For now, let’s think of it as a while loop that will run until Node has executed every line of code.

    一个 事件循环是一个连续运行的循环(就像while循环一样)。 它一次执行一个命令,稍后再执行。 现在,让我们将其视为一个while循环,它将一直运行到Node执行完每一行代码为止。

    Now, let’s take a look at how our code runs inside of Node.js instance.

    现在,让我们看一下我们的代码如何在Node.js实例中运行。

    console.log('Task 1');
    console.log('Task 2');
    // some time consuming for loop
    for(let i = 0; i < 1000000000; i++) {
    }
    console.log('Task 3');

    What happens when we run this code? It will first print out Task 1 then Task 2 and then it will run the time consuming for loop (we won’t see anything in the terminal for a couple seconds) and finally it will print out Task 3.

    当我们运行此代码时会发生什么? 它将首先打印出Task 1然后打印Task 2 ,然后将运行耗时的循环(在终端上几秒钟不会看到任何内容),最后将打印出Task 3

    Let’s look at a diagram of what’s actually happening.

    让我们看一下实际发生的情况。

    Node puts all our tasks into an Events queue and sends them one by one to the event loop. The event loop is single-threaded and it can only run one thing at a time. So it goes through Task 1 and Task 2 then the very big for loop and then it goes to Task 3. This is why we see a pause in the terminal after Task 2 because it is running the for a loop.

    Node将我们所有的任务放入一个事件队列,并将它们一个接一个地发送到事件循环。 事件循环是单线程的,一次只能运行一件事。 因此它先经过任务1和任务2,然后经过很大的for循环,然后进入任务3。这就是为什么我们在终端中看到任务2之后暂停的原因,因为它正在运行for循环。

    Now let’s do something different. Let’s replace that for loop with an I/O event.

    现在让我们做些不同的事情。 让我们用一个I / O事件代替for循环。

    console.log('Task 1');
    console.log('Task 2');
    fs.readFile('./ridiculously_large_file.txt', (err, data) => {
        if (err) throw err;
        console.log('done reading file');
        process.exit();
    });
    console.log('Task 3');

    Pro tip: you can generate a 100mb file in linux or mac just by running this command:  dd if=/dev/urandom of=ridiculously_large_file.txt bs=1048576 count=100.

    专家提示 :您只需运行以下命令即可在linux或mac中生成100mb文件: dd if=/dev/urandom of=ridiculously_large_file.txt bs=1048576 count=100

    We naturally assume that this will output something similar. Just like the for loop reading big files takes time, the execution on the event loop will take some time. However, we get something totally different.

    我们自然假设这将输出类似的内容。 就像for循环读取大文件需要时间一样,在事件循环上执行将花费一些时间。 但是,我们得到了完全不同的东西。

    Task 1
    Task 2
    Task 3
    done reading file

    But what caused this? How did Task 3 get executed before the file was read? Well let’s take a look at the visuals below to see what’s happening.

    但是是什么原因造成的呢? 在读取文件之前,如何执行任务3? 好吧,让我们看一下下面的视觉效果,看看发生了什么。

    I/O tasks, network requests, and database processes are classified as blocking tasks in Node.js. So whenever the event loop encounters these tasks it sends them off to a different thread and moves on to the next task in events queue.

    I / O任务,网络请求和数据库进程在Node.js中被归类为阻止任务。 因此,每当事件循环遇到这些任务时,它将它们发送到另一个线程,然后继续进行到事件队列中的下一个任务。

    A thread gets initiated from the thread pool to handle each blocking task. When it is done, it puts the result in a call-back queue.

    线程从线程池中启动以处理每个阻塞任务。 完成后,将结果放入回叫队列中。

    When the event loop is done executing everything in the events queue it will start executing the tasks in the call-back queue. So that’s why we see done reading file at the end.

    当事件循环完成后,将执行事件队列中的所有内容,它将开始执行回调队列中的任务。 这就是为什么我们看到最后done reading file的原因。

    是什么使单线程事件循环模型高效? ⚙️ (What makes the Single Threaded Event Loop Model efficient? ⚙️)

    JavaScript was created to do just simple things in web browsers such as form validation or simple animations. This is why it was built with a single-threaded event loop model. But running everything in one thread is considered as a disadvantage.

    创建JavaScript只是为了在Web浏览器中完成简单的事情,例如表单验证或简单的动画。 这就是为什么它是用单线程事件循环模型构建的。 但是将所有内容都运行在一个线程中被认为是不利的。

    In 2009 Ryan Dahl, the creator of Node, saw this simple event loop model as an opportunity to build a lightweight web server.

    2009年,Node的创建者Ryan Dahl将这种简单的事件循环模型视为构建轻量级Web服务器的机会。

    To better understand what problem Node.js solves we should look at what typical web servers were like before Node.js came into play.

    为了更好地理解Node.js解决的问题,我们应该看看Node.js投入使用之前典型的Web服务器是什么样的。

    This is how a traditional multi-threaded web application model handles a request:

    这是传统的多线程Web应用程序模型如何处理请求的方式:

    1. It maintains a thread pool (a collection of available threads)

      它维护一个线程池(可用线程的集合)
    2. When client request comes in, a thread is assigned

      客户端请求进入时,将分配一个线程
    3. This thread will take care of reading client requests, processing client requests, performing any blocking IO operations (if required) and preparing a response.

      该线程将负责读取客户端请求,处理客户端请求,执行任何阻塞的IO操作(如果需要)并准备响应。
    4. This thread is not free until a response is sent back

      在发送回响应之前,该线程不是空闲的

    The main drawback of this model is handling concurrent users. So let’s say if we have more users visiting our site than there are available threads then some users will need to wait until a thread frees up to get response.

    该模型的主要缺点是处理并发用户。 假设我们访问网站的用户多于可用线程,那么一些用户将需要等到线程释放以获取响应。

    If a lot of users are performing blocking I/O tasks then this wait time also increases. This is also very resource-heavy – so if we are expecting one million concurrent users we better make sure we have enough threads to handle those requests.

    如果许多用户正在执行阻止I / O任务,则此等待时间也会增加。 这也是非常重的资源-因此,如果我们期望有100万并发用户,则最好确保我们有足够的线程来处理这些请求。

    Moreover, the server itself start to slow down because of the increasing load. There’s also the overhead of context switching between threads, and writing applications to optimize threads' resource sharing can be painful.

    此外,由于负载增加,服务器本身开始变慢。 在线程之间进行上下文切换还存在开销,编写应用程序以优化线程的资源共享可能会很痛苦。

    Because of the single-threaded model Node.js, it doesn’t need to spin off new threads for every single request. Node.js also delegates blocking tasks to other components as we saw earlier. Since we don’t really care about many threads this makes Node.js very lightweight and ideal for microservice-based architecture.

    由于使用了单线程模型Node.js,因此不需要为每个请求都派生新线程。 如前所述,Node.js还将阻止任务委托给其他组件。 由于我们实际上并不关心很多线程,因此这使Node.js非常轻巧,非常适合基于微服务的体系结构。

    节点单线程模型的缺点 (Drawbacks of Node’s Single Threaded Model )

    The single-threaded event loop architecture uses resources efficiently but it does have some drawbacks.

    单线程事件循环体系结构有效地利用了资源,但确实存在一些缺点。

    The Node.js instance cannot immediately benefit from multiple cores in your CPU. A Java application can have immediate access to more memory as we upgrade our hardware but Node runs on a single thread.

    Node.js实例无法立即受益于CPU中的多个内核。 Java 当我们升级硬件时,应用程序可以立即访问更多内存,但是Node在单个线程上运行。

    This is 2020 and we are seeing more and more complicated web applications. What if our application needs to do complex computations, or run a machine learning algorithm? Or what if we want to run a complicated crypto algorithm? In this case we have to harness the power of multiple cores to increase performance.

    这是2020年,我们看到越来越复杂的Web应用程序。 如果我们的应用程序需要执行复杂的计算或运行机器学习算法怎么办? 或者,如果我们想运行复杂的加密算法怎么办? 在这种情况下,我们必须利用多核的功能来提高性能。

    Languages like Java and C# can programmatically initiate threads and harness the power of multiple cores. In Node.js that is not an option as we saw earlier. Node’s way of solving this problem is child_process.

    诸如JavaC#之类的语言可以以编程方式启动线程并利用多核的功能。 在Node.js中 ,这不是我们前面看到的选项。 Node解决此问题的方法是child_process

    节点中的子进程 (Child Process in Node)

    The child_process module gives Node the ability to spawn child processes by accessing operating system commands.

    child_process模块​​使Node能够通过访问操作系统命令来生成子进程。

    Let’s assume we have a REST endpoint that has a long-running function and we would like to use multiple cores in our processor to execute this function.

    假设我们有一个具有长期运行功能的REST端点,并且我们想在处理器中使用多个内核来执行此功能。

    Here’s our code:

    这是我们的代码:

    const { fork } = require('child_process');
     
    app.get('/endpoint', (request, response) => {
       // fork another process
       const process_ml_algo = fork('./process_data.js');
       const data = request.body.data;
       // send send the data to forked process
       process_ml_algo.send({ data });
       // listen to forked process 
       process.on('ml_algo', (result) => {
         log.info(`ml_algo executed with ${result}`);
       });
       return response.json({ status: true, sent: true });
    });
    // receive message from master process
    process.on('ml_algo', async (message) => {
        const result = await runMachineLearningProcess(message.mails); 
     
        // send response to master process
        process.send({ result: result });
    });

    In the example above we demonstrate how we can spin off a new process and share data between them. Using the forked process we can take advantage of multiple cores of CPU.

    在上面的示例中,我们演示了如何剥离新进程并在它们之间共享数据。 使用分叉的过程,我们可以利用CPU的多个核心。

    You can take a look at all the methods of child processes in the official node docs.

    您可以在官方节点docs中查看子进程的所有方法。

    Here is a diagram of how child processes work

    这是子进程的工作原理图

    child_process is a good solution but there’s another option. The child_process module spins off new instances of Node to distribute the workload, and all these instances will each have 1 event loop 1 thread and 1 process.

    child_process是一个很好的解决方案,但是还有另一个选择。 child_process模块派生出Node的新实例以分配工作量,所有这些实例将分别具有1个事件循环,1个线程和1个进程。

    In 2018 Node.js introduced worker_thread. This module gives Node the ability to have:

    在2018年,Node.js引入了worker_thread 。 该模块使Node具有以下功能:

    • 1 Process

      1过程
    • Multiple threads

      多线程
    • 1 Event Loop per thread

      每个线程1个事件循环

    Yes! You read that right 😄.

    是! 你没看错😄。

    const { Worker, workerData, isMainThread, parentPort } = require('worker_threads');
     
    if (isMainThread) {
      const worker1 = new Worker(__filename, { workerData: 'Worker Data 1'});
      worker1.once('message', message => console.log(message));
      const worker2 = new Worker(__filename, { workerData: 'Worker Data 2' });
      worker2.once('message', message => console.log(message));
    } else {
      parentPort.postMessage('I am ' + workerData);
    }

    We check if it is the main thread and then create two workers and pass on messages. On the worker thread the data gets passed on through the postMessage method and the workers execute the command.

    我们检查它是否为主线程,然后创建两个worker并传递消息。 在工作线程上,数据通过postMessage方法传递,工作线程执行命令。

    Since worker_threads makes new threads inside the same process it requires fewer resources. Also we are able to pass data between these threads because they have the shared memory space.

    由于worker_threads在同一进程内创建新线程,因此需要较少的资源。 我们也能够在这些线程之间传递数据,因为它们具有共享的内存空间。

    As of January 2020 worker_threads are fully supported in the Node LST version 12. I highly recommend reading this post if you want to learn more about worker_threads.

    从2020年1月开始 ,Node LST版本12中完全支持worker_threads 。如果您想了解有关worker_threads的更多信息,我强烈建议阅读此文章

    结语 (Wrapping up)

    And that’s it!

    就是这样!

    In this article we looked at how the event loop model works in Node.js, and we discussed some of the pros and cons of the single-threaded model and looked at a couple of solutions.

    在本文中,我们研究了事件循环模型在Node.js中的工作方式,并讨论了单线程模型的一些优缺点,并研究了几种解决方案。

    We didn’t go over all the functionalities of child_process and worker_threads. But I hope that this article provided you with a brief introduction to these concepts and why they exist. Please let me know if you have any feedback. Until next time 👋👋

    我们没有讨论child_process和worker_threads的所有功能。 但我希望本文向您简要介绍了这些概念以及它们为什么存在。 如果您有任何反馈意见,请告诉我。 直到下次time

    翻译自: https://www.freecodecamp.org/news/a-hands-on-guide-to-event-loop-child-process-and-worker-threads-in-node-js/

    node的事件循环是线程吗

    展开全文
  • Qode是Node.js的一个经过轻微修改的分支,它允许Node的事件循环与Qt或任何其他Gui事件循环合并。 它旨在与@nodegui/nodegui一起使用。 Qode通过允许通过NodeJS插件进行消息循环注入来实现此目的。 v2.0中的更改 在...
  • 摸鱼前端的自检(二)深入浏览器和node的事件循环

    千次阅读 多人点赞 2019-11-04 11:16:13
    浏览器事件循环node事件循环 node和js都保持了单线程特点,javascript在最初设计时设计成了单线程,为什么不是多线程呢?如果多个线程同时操作DOM那岂不会很混乱? 有兴趣同学可以去我github,里面有我分享...

    浏览器事件循环和node事件循环

    node和js都保持了单线程的特点,javascript在最初设计时设计成了单线程,为什么不是多线程呢?如果多个线程同时操作DOM那岂不会很混乱?

    有兴趣的同学可以去我的github,里面有我的分享的学习过程和blog.
    github.com/193Eric


    JS事件循环

    首先我们要知道js是干嘛的,和用户通过浏览器进行交互,如果同时两个任务操作一个dom,那不就乱套了。单线程意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务

    但是呢,随着技术的进步,单线程确实限制了javascript的发展,所以因此出现了一个叫web worker的东西,从表面上来说是把javascript做成了多线程语言(为什么说是表面上,因为web orker 是受主线程控制的,等同于子线程,只能做一些次要的计算,处理数据的任务,无法操作I/O,本质上javascript还是单线程语言)

    关于流程我画了幅图:

    在这里插入图片描述

    1. 首先所有同步任务都在主线程上执行,形成一个执行栈(就是那个大圆筒里面的stack,里面存在同步任务)。

    2. 在这个主线程之外,存在一个事件队列,只要异步任务有了结果,就往事件队列里面放置一个任务。(比如说setTimeout时间到了,就往队列里面放执行的函数)

    3. 如果执行栈中的任务执行完毕,就会去读取任务队列,推入栈,继续执行。

    4. js是单线程的,但是浏览器是多线程的,不同的异步任务是由不同的浏览器内核的模块来执行的

    onclick 由浏览器内核的 DOM Binding ;setTimeout 会由浏览器内核的 timer 模块;ajax 则会由浏览器内核的 network 模块

    1. event loop里面至少有两个队列,一个是macrotask(宏任务),一个是microtask(微任务)。读取任务队列的时候会优先读取微任务,然后执行完之后才会读取宏任务 (ps: 特别的,如果微任务在执行过程中,又有微任务进了队列,执行完之后,还会优先读取微任务,直到所有微任务读取完,才会读取宏任务 ~~~ 没错 就是这么卑微…)

    浏览器的事件循环可以理解为:

    • 先执行一个 MacroTask,然后执行所有的 MicroTask;

    • 再执行一个 MacroTask,然后执行所有的 MicroTask;


    node的事件循环

    node的事件循环要比浏览器中的要复杂一点,node中事件循环的实现是依靠的libuv引擎(我们现在主要说node 11 版本之前的)。

    其实nodejs与浏览器的区别,就是nodejs的 MacroTask 分好几种,而这好几种又有不同的 task queue,而不同的 task queue 又有顺序区别,而 MicroTask 是穿插在每一种【注意不是每一个!】MacroTask 之间的。

    注意,在 node 11 版本中,node 下 Event Loop 已经与浏览器趋于相同。

    注意,在 node 11 版本中,node 下 Event Loop 已经与浏览器趋于相同。

    注意,在 node 11 版本中,node 下 Event Loop 已经与浏览器趋于相同。

    下面是node的事件循环图解:

    
       ┌───────────────────────┐
    
    ┌─>│        timers         │<————— 执行 setTimeout()、setInterval() 的回调
    
    │  └──────────┬────────────┘
    
    |             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
    
    │  ┌──────────┴────────────┐
    
    │  │     pending callbacks │<————— 执行由上一个 Tick 延迟下来的 I/O 回调(待完善,可忽略)
    
    │  └──────────┬────────────┘
    
    |             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
    
    │  ┌──────────┴────────────┐
    
    │  │     idle, prepare     │<————— 内部调用(可忽略)
    
    │  └──────────┬────────────┘     
    
    |             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
    
    |             |                   ┌───────────────┐
    
    │  ┌──────────┴────────────┐      │   incoming:   │ - ( 获取新的 I/O 事件;nodejs这时会适当进行阻塞;)
    
    │  │         poll          │<─────┤  connections, │ 
    
    │  └──────────┬────────────┘      │   data, etc.  │ 
    
    │             |                   |               | 
    
    |             |                   └───────────────┘
    
    |             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
    
    |  ┌──────────┴────────────┐      
    
    │  │        check          │<————— setImmediate() 的回调将会在这个阶段执行
    
    │  └──────────┬────────────┘
    
    |             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
    
    │  ┌──────────┴────────────┐
    
    └──┤    close callbacks    │<————— socket.on('close', ...)
    
       └───────────────────────┘
    
    

    node的事件循环可以理解为:

    • 先执行所有类型为 timers 的 MacroTask,执行NextTick,然后执行所有的 MicroTask(

    • 进入 poll 阶段,执行几乎所有 MacroTask,执行NextTick,然后执行所有的 MicroTask;

    • 再执行所有类型为 check 的 MacroTask,执行NextTick,然后执行所有的 MicroTask;

    • 再执行所有类型为 close callbacks 的 MacroTask,执行NextTick,然后执行所有的 MicroTask;

    • 至此,完成一个 Tick,回到 timers 阶段;


    node事件需要注意的点

    一.setTimeout 与 setImmediate

    我们根据上面的可以看到,timer在check之前,但是实际情况是这两个执行的顺序会因为它们被调用时的上下文而有所不同。

    比如说下面代码:

    
    fs.readFile(__filename, () => {
    
      setTimeout(() => {
    
        console.log('timeout')
    
      }, 0)
    
      setImmediate(() => {
    
        console.log('immediate')
    
      })
    
    })
    
    

    我们可以看到,两个事件是放在io循环里面,所以当前的阶段处于poll,所以check会在timer之前。

    如果我们在非 I/O 循环中运行下面的脚本(即在主模块中),他俩的顺序是不固定的,因为会受到进程性能的影响:

    
    setTimeout(() => {
    
      console.log('timeout')
    
    }, 0)
    
    
    
    setImmediate(() => {
    
      console.log('immediate')
    
    })
    
    

    这段代码的执行结果是不定的。

    虽然 setTimeout 延时为 0,但是一般情况 Node 把 0 会设置为 1ms,所以,当 Node 准备 event loop 的时间大于 1ms 时,进入 timers 阶段时,setTimeout 已经到期,则会先执行 setTimeout;反之,若进入 timers 阶段用时小于 1ms,setTimeout 尚未到期,则会错过 timers 阶段,先进入 check 阶段,而先执行 setImmediate

    二.Poll阶段

    poll 阶段主要有两个功能:

    • 获取新的 I/O 事件,并执行这些 I/O 的回调,之后适当的条件下 node 将阻塞在这里

    • 当有 immediate 或已超时的 timers,执行它们的回调

    poll阶段是获取并执行几乎所有的io事件,是event loop的重要阶段,它首先执行完自己的poll queue的callbacks,然后结束poll阶段,然后执行:

    1.setImmediate 的 queue 不为空,则进入 check 阶段.

    2.setImmediate 的 queue 为空,但是 timers 的 queue 不为空,则直接进入 timers 阶段,然后又来到 poll.

    3.setImmediate 的 queue 为空,timers 的 queue 也为空,此时会阻塞在这里,因为无事可做

    三.nextTick

    nextTick队列是穿插在node事件循环的每一个队列中,从上面可以看到,每执行一个一个事件阶段的宏任务之后就会执行nextTick队列的事件,然后再执行微任务,全部执行完之后再进入下一个事件阶段。

    
       ┌───────────────────────┐
    
    ┌─>│        timers         │
    
    │  └──────────┬────────────┘
    
    |      nextTick(队列执行)
    
    │  ┌──────────┴────────────┐
    
    │  │     I/O callbacks     │
    
    │  └──────────┬────────────┘
    
    |       nextTick(队列执行)
    
    │  ┌──────────┴────────────┐
    
    │  │     idle, prepare     │
    
    │  └──────────┬────────────┘      
    
    |      nextTick(队列执行)         ┌───────────────┐
    
    │  ┌──────────┴────────────┐      │   incoming:   │
    
    │  │         poll          │<─────┤  connections, │
    
    │  └──────────┬────────────┘      |               |
    
    |      nextTick(队列执行)         │   data, etc.  │
    
    │  ┌──────────┴────────────┐      └───────────────┘
    
    │  │        check          │
    
    │  └──────────┬────────────┘
    
    |       nextTick(队列执行)
    
    │  ┌──────────┴────────────┐
    
    └──┤    close callbacks    │
    
       └───────────────────────┘
    
    

    上面的图其实没有写清楚,微任务队列追加在process.nextTick队列的后面,也属于本轮循环。

    简单来说 每一个事件阶段都是本身的宏任务 > nexTick > 本身的微任务

    下面有一道题

    
    console.log('main')
    
    
    
    process.nextTick( () => {
    
      console.log('process.nextTick1')
    
    })
    
    
    
    setTimeout(() => {
    
      console.log('setTimeout')
    
      process.nextTick(() => {
    
        console.log('process.nextTick2')
    
      })
    
    }, 0)
    
    
    
    new Promise((resolve, reject) => {
    
      console.log('promise')
    
      resolve()
    
    }).then(() => {
    
      console.log('promise then')
    
    })
    
    
    
    console.log('main2')
    
    

    1、首先先找全局的宏任务(Main 进程),所以先是打印main,然后打印promise,再是main2;

    2、宏任务找完之后,找nextTick 招到了全局nextTick只有一个,打印process.nextTick1;

    3、nextTick找完,接着寻找全局微任务,所以打印promise then;

    4、全局的找完之后,进入下一个阶段timer,招到了setTimeout 所以打印setTimeout;

    5、然后寻找NextTick找到并打印process.nextTick2,然后寻找微任务,没有。结束

    所以答案是

    main

    promise

    main2

    process.nextTick1

    promise then

    setTimeout

    process.nextTick2

    展开全文
  • 浏览器中有事件循环node 中也有,事件循环node 处理非阻塞 I/O 操作机制,node事件循环的实现是依靠libuv引擎。由于 node 11 之后,事件循环的一些原理...
  • 转自浏览器与Node的事件循环(EventLoop)有何区别? 又被node的eventloop坑了,这次是node的锅 前言 本文我们将会介绍 JS 实现异步的原理,并且了解了在浏览器和 Node 中 Event Loop 其实是不相同的。 想阅读更多优质...
  • Node基础学习系列 上一篇:Node基础学习(十四):Node的简单...Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数. c.
  • 浏览器中有事件循环,node 中也有,事件循环是 node 处理非阻塞 I/O 操作的机制,node中事件循环的...宏任务和微任务node 中也有宏任务和微任务,与浏览器中的事件循环类似。macro-task 大概包括:setTimeoutsetInte...
  • Javascript中的事件循环 javascript是一门单线程的非阻塞的脚本语言。单线程,即js代码在执行的任何时候,都只有一个主线程来处理所有任务。非阻塞,只要指的是执行异步任务(如I/O事件)时,主线程会挂起这个任务...
  • Nodejs原文文档:...接触过事件循环的同学大都会纠结一个点,就是在Node中setTimeout和setImmediate执行顺序随机性。 比如说下面这段代码: setTimeout(...
  • 关于微任务和宏任务在浏览器执行顺序是这样: 执行一只task(宏任务) 执行完micro-task队列 (微任务) 如此循环往复下去 浏览器task(宏任务)执行顺序在 html#event-loops 里面有讲就不翻译了 常见 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,153
精华内容 461
关键字:

node的事件循环