首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Nodejs深度探秘:event loop的本质和异步代码中的Zalgo问题

在主线程的循环中,它会不断轮询特定队列,看看是否有数据可以处理,如果有那么它就从队列中取下来,然后将数据进行处理后发送给需要的客户端。...由于主线程不用长时间阻塞,因此它能够在给定时间内对大量的客户端请求进行响应,这是它能实现高并发的原因。 主线程不断轮询特定队列是否有数据的过程也叫event loop。...在每个阶段,主线程会从对应队列中获取数据返回给客户端,或者是将存储在队列中的回调函数进行执行,当队列清空,或者访问的队列元素超过给定值后就会进入下一个阶段。...第二个阶段是操作系统在某项情况下需要通知特定事件给NodeJS,例如TCP连接请求被拒绝,数据库连接失败等;idle阶段属于nodejs内部使用,主线程会执行一些nodejs内部特定回调函数执行一些内部事务...它本质上跟setImmediate没有什么区别,只不过后者属于event loop的特定阶段而前者不属于event loop,因此它最大的作用是让代码在主线程进入下一轮循环前做一些操作,例如释放掉一些没用的资源

1.3K10

息息相关的 JS 同步,异步和事件轮询

调用栈 调用堆栈顾名思义是一个具有LIFO(后进先出)结构的堆栈,用于存储在代码执行期间创建的所有执行上下文。 JS 只有一个调用栈,因为它是一种单线程编程语言。...但是回调不会立即执行,这就是事件轮询开始的地方。 事件轮询 事件轮询的工作是监听调用堆栈,并确定调用堆栈是否为空。如果调用堆栈是空的,它将检查消息队列,看看是否有任何挂起的回调等待执行。...'); }); 对于DOM事件,事件侦听器位于web api环境中,等待某个事件(在本例中单击event)发生,当该事件发生时,回调函数被放置在等待执行的消息队列中。...同样,事件轮询检查调用堆栈是否为空,并在调用堆栈为空并执行回调时将事件回调推送到堆栈。 延迟函数执行 咱们还可以使用setTimeout来延迟函数的执行,直到堆栈清空为止。...0秒后,bar()回调被放入等待执行的消息队列中,但是它只会在堆栈完全空的时候执行,也就是在baz和foo函数完成之后。

9.8K31
  • 您找到你想要的搜索结果了吗?
    是的
    没有找到

    试图解释清楚【JavaScript Event Loop】

    (队列的特点是先进先出) 当调用栈为空时,event loop会消息队列中的下一个消息 被处理的消息被移出队列, 消息被作为参数调用与之关联的回调函数 同时为该函数调用向调用栈添加一个新的栈帧 调用栈再次为空时...浏览器和NodeJS基于不同的技术实现了各自的Event Loop。 浏览器的Event Loop模型是在html5的规范[2]中明确定义的,具体的实现由浏览器厂商来做。...因此,不是保证回调在n毫秒内必须执行,而是保证回调在n毫秒之后被添加到消息队列,具体什么时候执行,取决于消息队列中待处理的消息 和 调用栈中已有的函数。...,即每次event loop处理消息执行回调所占用的时间 小于 16.67 毫秒。...后续的task暂不处理 每当调用栈清空后,重复2-3步骤 两个重点: 微任务阻塞浏览器:如果执行微任务期间,不停的有新的微任务,会导致浏览器阻塞 微任务的执行会因为JS堆栈的情况有所不同,要根据调用栈是否清空去判断微任务是否会执行

    63531

    在nodejs中事件循环分析

    事件循环 当 Node.js 启动时,它将初始化事件循环机制,处理提供的输入脚本,该脚本可能会进行异步 API 调用、计划计时器或调用,然后开始处理事件循环。...虽然每个阶段都有自己的特殊性,但通常,当事件循环进入给定阶段时,它将执行特定于该阶段的任何操作,然后在该阶段的队列中执行回调,直到队列用尽或执行最大回调数。...当队列已用尽或达到回调限制时,事件循环将进入下一阶段,依此类推。 由于这些操作中的任何一个都可能计划更多操作,并且轮询阶段处理的新事件由内核排队,因此可以在处理轮询事件时对轮询事件进行排队。...而js引擎要做的是将错误传递回用户,但只有在允许用户执行其余代码之后。...为了实现这一点,允许JS调用堆栈展开,然后立即执行提供的回调,该回调允许人们在没有遇到RangeError: Maximum call stack size exceeded from v8这个异常的时候执行

    4K00

    JavaScript执行机制

    微任务一个 微任务(microtask)就是一个简短的函数,当创建该函数的函数执行之后,并且 只有当 Javascript 调用栈为空,而控制权尚未返还给被 user agent 用来驱动脚本执行环境的事件循环之前...一直执行直至执行栈为空,微任务队列为空,GUI渲染线程接管,进行GUI渲染。...注意这个步骤,不会重复检测宏任务队列,在微任务队列为空之后,将会进入GUI渲染阶段,剩余的推送过来的宏任务会在下一个loop进行执行。...Promise.then属于微任务,会在事件处理线程注册到Event Table中,在Promise的状态改变前不会执行,进行下一次loop。...第二轮loop,清空完微任务队列之后取出宏任务队列中的children5所属宏任务进行执行,输出children5,然后将第一轮中的Promise状态置为完成态,事件处理线程会将其对应的.then的回调函数放入到对应的微任务队列中

    39933

    NodeJs 事件循环-比官方翻译更全面

    这就是事件循环(Event Loop Explained) Node.js启动时,它将初始化事件循环,处理提供的输入脚本(或放入REPL,本文档未涵盖),这些脚本可能会进行异步API调用,调度计时器或调用...尽管每个阶段都有其自己的特殊方式,但是通常,当事件循环进入给定阶段时,它将执行该阶段特定的任何操作,然后在该阶段的队列中执行回调,直到队列耗尽或执行回调的最大数量为止。...在此,将操作定义为在C/C ++处理程序基础下过渡并处理需要执行的JavaScript。...我们正在做的是将错误传递回用户,但只有在我们允许其余用户的代码执行之后。...为此,允许JS调用堆栈展开,然后立即执行所提供的回调,该回调可以对process.nextTick进行递归调用,而不会达到RangeError:v8超出最大调用堆栈大小。

    2.2K60

    关于NodeJS工作原理的五个误解

    NodeJS 事件循环是 NodeJS 的核心,它为 NodeJS 提供了异步的,非阻塞的 I/O 机制。它以特定顺序处理来自不同类型的异步事件的完成事件。...相反,NodeJS Event Emitter 是一个核心的 NodeJS API,它允许你将监听器函数附加到一个特定的事件,这个事件一旦触发就会被调用。...I am the last log line 由于 event emitter 同步执行所有事件处理函数,因此 I am the last log line 在调用所有监听函数完成之后才会打印。...误解2 - 所有接受回调的函数都是异步的 函数是同步的还是异步的取决于函数在执行期间是否创建异步资源。...同步函数在执行的整个过程中都会占用堆栈,方法是禁止其他任何人占用堆栈直到return 为止。相反,异步函数调度一些异步任务并立即返回,因此将自身从堆栈中删除。

    1.6K20

    Node.js中的事件循环,定时器和process.nextTick()

    每个阶段都有一个待执行回调函数的FIFO队列, 虽然每个阶段都不尽相同,总体上说,当事件循环到当前阶段时,它将执行特定于该阶段的操作,然后就会执行被压入当前队列中的回调函数, 直到队列被清空或者达到最大的调用上限...在任意两个阶段之间,Node.js都会检查是否还有在等待中的异步I/O事件或者定时器,如果没有就会干净得关掉它。...阶段的细节 timers 定时器将会在一个特定的时间之后执行相应的回调,而不是在一个通过开发者设置预期的时间执行。...相反的,nextTickQueue会在当前的操作执行完成后运行,而不必在乎是在某一个特定的阶段 回到我的图示,每次你在一个阶段中调用process.nextTick()的时候,所有的回调都会在事件循环进入到下一个阶段的时候被处理完毕...有时在调用堆栈已解除但在事件循环继续之前,必须允许回调运行。

    2.4K30

    JavaScript——事件循环机制

    综上所述,检查调用栈是否为空以及讲某个任务添加到调用栈中的个过程就是event loop,这就是JavaScript实现异步的核心。...2.2 event loop过程 检查macrotask队列是否为空,非空则到2,为空则到3 执行macrotask中的一个任务 继续检查microtask队列是否为空,若有则到4,否则到5 取出microtask...pending callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调 idle, prepare 阶段:仅node内部使用 poll 阶段:获取新的I/O事件, 适当的条件下node...都会被加到这一个宏队列中,但是在NodeJS中,不同的macrotask会被放置在不同的宏队列中。...,比如Promise等 在浏览器中,也可以认为只有一个微队列,所有的microtask都会被加到这一个微队列中,但是在NodeJS中,不同的microtask会被放置在不同的微队列中。

    12811

    2020-5-27-Nodejs源码阅读——事件循环

    最近看到Nodejs的事件循环,发现网上的一些文档和描述都不够清晰。 所以今天来和大家一起从源码出发了解下Nodejs的事件循环机制。...主要有以下几点: 文档说了没有timer被调度时的情况,那么如果有timer被调度,会怎么样呢? poll queue状态为非空的情况下,回调执行完毕后,还会执行check阶段么?...Nodejs源码分析 nodejs的时间循环依赖于libuv,通过调用uv_run(env->event_loop(), UV_RUN_DEFAULT)这个方法启动。...这里我们发现这个阶段的处理器不是在一个队列里,而是一个最小堆中。 这个也很好理解,timer的执行依赖于最先到达指定时间的顺序,而不是依赖于代码中的添加顺序。...这里主要是为了处理setInterval的情况,实现循环计时。 这段源码我们发现: timer阶段并不是维护FIFO的队列,而是一个最小堆 回调在这个循环内同步执行,处理所有到时定时器。

    98630

    Event Loop(node.js)

    从用户代码入口开始,执行完所有同步代码后进入事件循环,在事件循环里的每一个阶段都查看该阶段的任务队列是否为空,如果不为空则尝试同步执行(以先进先出顺序一个一个执行)所有队列里的任务直到队列为空。...(因为这阶段在处理事件的时候可以产生新事件入队而导致队列一直不为空从而阻塞事件循环,所以有最大次数限制)。...setImmediate 通过 setImmediate 设置的回调在这里排队执行。 'close' 事件 on('close') 事件调用的回调在这里排队执行。...,在每个阶段结束的时候都会查看这个队列是否为空,如果不为空就一个个执行里面所有的任务直到队列为空。...但是它们的存在又确实是必要的: 用户要在事件循环继续之前处理错误、清理资源 在当前执行栈之后且在事件循环之前需要执行一个回调 官方文档举了这样一个例子: const EventEmitter = require

    81920

    JavaScript的工作原理:引擎,运行时和调用堆栈的概述

    接下来,我们将介绍一下非常流行的 事件循环(event loop) 和 回调队列(callback queue)。...之后,步骤如下: ? 调用栈中的每个条目称为堆栈帧(Stack Frame)。 这正是抛出异常时堆栈跟踪的构造方式 - 它基本上是异常发生时调用栈的状态(异常后的全过程)。...但是,此函数是递归的,并且在没有任何终止条件的情况下开始调用自身(产生无限循环)。因此,在执行的每个步骤中,相同的函数会一遍又一遍地添加到调用堆栈中。它看起来像这样: ?...然而,在某些时候,调用堆栈中的函数调用数量超过了调用堆栈的实际大小,浏览器会抛出看起来像这样的错误: ?...Concurrency & the Event Loop 如果在调用堆栈中有函数调用需要花费大量时间才能处理,会发生什么?例如,在浏览器中使用 JavaScript 进行一些复杂的图像转换。

    1.5K31

    Nodejs探秘:深入理解单线程实现高并发原理

    于是在我们刚接触Nodejs时,会有所疑问: 1、为什么在浏览器中运行的Javascript 能与操作系统进行如此底层的交互?   2、nodejs 真的是单线程吗?...3、如果是单线程,他是如何处理高并发请求的? 4、nodejs 事件驱动是如何实现的? 等等。。。 看到这些问题,是否有点头大,别急,带着这些问题我们来慢慢看这篇文章。...我们在 Javascript 中调用的方法,最终都会通过 process.binding 传递到 C/C++ 层面,最终由他们来执行真正的操作。Node.js 即这样与操作系统进行互动。...、setInterval)的回调 I/O callbacks 阶段:执行一些系统调用错误,比如网络通信的错误回调 idle, prepare 阶段:仅node内部使用 poll 阶段:获取新的I/O事件...、Nodejs与操作系统交互,我们在 Javascript 中调用的方法,最终都会通过 process.binding 传递到 C/C++ 层面,最终由他们来执行真正的操作。

    2.2K30

    深入nodejs的event-loop

    只有在特定情况下(某个操作系统对某种类型I/O没有提供相应的异步接口的时候),libuv才会使用线程池中的线程+轮询来实现异步I/O。...当Event Demultiplexer从操作系统中拿到I/O处理结果后,它就会通知event loop将相应的callback/handler入队到相应的队列中。...跟浏览器环境中的setTimeout和setInterval方法一样,调用时候传入的延迟时间并不是回调确切执行的时间。...poll在进入轮询阶段之前,event loop会检查timer callback queue是否为空,如果不为空的话,那么event loop就会回退到timer阶段,依次执行所有的timer callback...只有当这两个队列都为空的情况下,nodejs才会进入event loop。 认真观察的话,我们会发现,这两个队列的支持递归入队的特性跟浏览器的event loop中micrtask队列是一样的。

    72930

    搞懂JavaScript引擎运行原理

    异步 — 同时做多个事,JS通过浏览器API模拟异步行为 事件循环(Event Loop) - 浏览器API完成函数调用的过程,将回调函数推送到回调队列(callback queue),然后当堆栈为空时...是否发生错误,因为b在a之后声明或者一切正常? console.log 打印的变量又是怎么样?...函数执行结束后会从堆栈中弹出,并且它的执行上下文被垃圾收集回收(闭包除外)。 当调用堆栈为空时,它将从事件队列中获取事件。...然后停留在队列中,只有当调用堆栈(call stack)为空时才会被压入堆栈。 ? 代码示例 要熟悉JS引擎,最好的方法就是使用它,再来些有意义的例子。...,会发生什么,回调队列被会阻塞,因为只能在调用堆栈为空时添加回调队列。

    87720

    深入nodejs的event-loop_2023-03-15

    只有在特定情况下(某个操作系统对某种类型I/O没有提供相应的异步接口的时候),libuv才会使用线程池中的线程+轮询来实现异步I/O。...当Event Demultiplexer从操作系统中拿到I/O处理结果后,它就会通知event loop将相应的callback/handler入队到相应的队列中。...跟浏览器环境中的setTimeout和setInterval方法一样,调用时候传入的延迟时间并不是回调确切执行的时间。...poll在进入轮询阶段之前,event loop会检查timer callback queue是否为空,如果不为空的话,那么event loop就会回退到timer阶段,依次执行所有的timer callback...只有当这两个队列都为空的情况下,nodejs才会进入event loop。 认真观察的话,我们会发现,这两个队列的支持递归入队的特性跟浏览器的event loop中micrtask队列是一样的。

    63220

    Nodejs探秘:深入理解单线程实现高并发原理

    于是在我们刚接触Nodejs时,会有所疑问: 1、为什么在浏览器中运行的Javascript能与操作系统进行如此底层的交互? 2、nodejs 真的是单线程吗?...3、如果是单线程,他是如何处理高并发请求的? 4、nodejs 事件驱动是如何实现的? 等等。。。 看到这些问题,是否有点头大,别急,带着这些问题我们来慢慢看这篇文章。...我们在 Javascript中调用的方法,最终都会通过 process.binding 传递到 C/C++ 层面,最终由他们来执行真正的操作。Node.js 即这样与操作系统进行互动。...timers 阶段:这个阶段执行timer(setTimeout、setInterval)的回调 I/O callbacks 阶段:执行一些系统调用错误,比如网络通信的错误回调 idle, prepare...因为是源码解析,所以具体的我就不多说,大家只可以看文档: 官方文档 总结: 1、Nodejs与操作系统交互,我们在 Javascript中调用的方法,最终都会通过 process.binding 传递到

    1.2K20

    深入研究 Node.js 的回调队列

    调用栈,事件循环和回调队列 调用栈被用于跟踪当前正在执行的函数以及从何处开始运行。当一个函数将要执行时,它会被添加到调用堆栈中。这有助于 JavaScript 在执行函数后重新跟踪其处理步骤。...同时事件循环会连续检查调用栈是否为空,以便可以从回调队列中提取一个函数并添加到调用栈中。事件循环仅在执行所有同步操作之后才检查队列。 那么,事件循环是按照什么样的顺序从队列中选择回调函数的呢?...完成后,它们将会被转移到 IO 回调队列中,来进行事件循环,以转移到调用栈中执行。...尽管首先填充了检查队列,但只有在 IO 队列为空之后才考虑使用它。所以在 setImmediate 之前,将 readFile 输出到控制台。...这样做的原因是此时 IO 队列为空。请记住,在执行 IO 队列中的所有的函数之后,将会立即运行检查队列回调。 总结 JavaScript 是单线程的。

    3.8K10

    Nodejs探秘:深入理解单线程实现高并发原理

    于是在我们刚接触Nodejs时,会有所疑问: 1、为什么在浏览器中运行的Javascript 能与操作系统进行如此底层的交互?   2、nodejs 真的是单线程吗?...3、如果是单线程,他是如何处理高并发请求的? 4、nodejs 事件驱动是如何实现的? 等等。。。 看到这些问题,是否有点头大,别急,带着这些问题我们来慢慢看这篇文章。...· Node.js 标准库,这部分是由 Javascript 编写的,即我们使用过程中直接能调用的 API。在源码中的 lib 目录下可以看到。...我们在 Javascript 中调用的方法,最终都会通过 process.binding 传递到 C/C++ 层面,最终由他们来执行真正的操作。Node.js 即这样与操作系统进行互动。...主线程不断的检查事件队列中是否有未执行的事件,直到事件队列中所有事件都执行完了,此后每当有新的事件加入到事件队列中,都会通知主线程按顺序取出交EventLoop处理。

    3.1K41

    动图学JS异步: Promises & AsyncAwait

    因此在getImage示例中,我们可以链式调用多个then方法,把处理过的image对象传入到下一个回调。这样我们就彻底甩脱了回调地狱,得到一个整洁的链式回调。 ? 完美!...然后事件循环会去检查macrotasks队列是否为空,不为空,依次将它们入栈到调用堆栈、执行完后弹出。 接下来我们跑一些实际的代码论证下。 ?...并打印在控制台,并从调用堆栈弹出。事件循环继续往下执行. ? 此时,事件循环或者说JS引擎发现调用堆栈为空,它会检查是否有在microtask队列中排队的任务!...结果发现确实有,promise的then回调在等待执行!于是它被弹出到调用堆栈后,由于它会记录promise之前resolve()中的值,因此打印出Promise!在控制台并且从调用堆栈弹出。 ?...JS引擎看到调用堆栈是空的,所以它会再次检查microtask队列,查看是否还有任务在进行排队。发现没有,microtask队列也是是空的。

    1.1K20
    领券