Yihui’s Blog

Node.js 前端面试题

参照

https://juejin.cn/post/6844904071501971469

https://juejin.cn/post/6844903764202094606

node-进程通信 :star:

Node.js 与 JavaScript 有什么不同?

(宿主环境)

img

什么时候用 Node.js?

Node.js 是异步的、事件驱动的、非阻塞的和单线程的,使得它成为开发下面应用程序的完美候选:

  • 实时应用程序,如聊天和提供实时更新的应用程序
  • 将视频或其他多媒体内容流式传输给大量观众的流式应用程序
  • 其他 I/O 密集型应用程序,如协作平台
  • 遵循微服务架构的网络后端

然而,Node.js 的特性使得它对于其他类型的应用程序来说不是一个理想的选择。执行 CPU 密集型任务的应用程序(如复杂的数学计算)在使用 CPU 时表现不佳,因为 Node.js 是单线程的。

如果你想了解更多这方面的信息,请查看我们的文章 Node.js 架构以及何时在项目中使用。

事件循环是什么?

单线程的 Node.js 必须是非阻塞的,以防止线程阻塞在需要很长时间才能完成的任务上,事件循环负责实现这种非阻塞行为,它使用应用程序线程调度挂起的任务。

Node.js 在任务完成时通过回调来处理异步函数返回的响应。与创建任务的事件类似,任务完成后也会发出一个事件。Node.js 将需要处理的事件添加到事件队列。

事件循环对事件队列中的事件进行迭代,并安排何时执行其关联的回调函数。

流是什么?

Stream 流是从源读取或写入数据并将其传输到连续流目标的管道。有四种类型:

  • 可读

  • 可写的

  • 可读写

  • 先写入,再读出来

每个流也是一个 EventEmitter。这意味着流对象可以在流上没有数据、流上有可用数据或流中的数据在程序刷新时发出事件。

readFile 和 createReadStream 函数有什么区别

readFile 函数异步读取文件的全部内容,并存储在内存中,然后再传递给用户。

createReadStream 使用一个可读的流,逐块读取文件,而不是全部存储在内存中。

与 readFile 相比,createReadStream 使用更少的内存和更快的速度来优化文件读取操作。如果文件相当大,用户不必等待很长时间直到读取整个内容,因为读取时会先向用户发送小块内容。

如何处理 Node.js 中未捕获的异常?

我们可以在进程级别捕获应用程序中未捕获的异常。为此将侦听器附加到 process 全局对象:

process.on("uncaughtException", (err) => {  console.log("exception caught: ", err);});

Node.js 能否充分利用多核处理器?

(默认的)Node.js 应用程序总是单线程的,即使在多核处理器上运行,应用程序也能只使用一个处理器。

但是 Node.js 的核心模块之一 Cluster 支持 Node.js 应用程序开启多核,允许我们创建多个工作进程,这些进程可以在多个内核上并行运行,并共享一个端口来侦听事件。

每个进程使用 IPC 与主线程通信,并根据需要将服务器句柄传递给其他进程。主进程可以侦听端口本身并以循环方式将每个新连接传递给子进程,也可以将端口分配给子进程以便子进程侦听请求。

单线程与多线程网络后端相比有哪些好处?

尽管 Node.js 是单线程的,但是大多数用于后端开发的编程语言都提供多线程来处理应用程序操作。

为什么单线程有利于后端开发?

  • 开发人员更容易实现应用程序。我们的应用程序在生产过程中不会突然遇到意外的竞争条件。
  • 单线程应用程序易于扩展。
  • 它们可以毫不延迟地在一个时刻收到的大量用户请求提供服务。相比之下,当流量较大时,多线程后端必须等待线程池中的线程释放,才能为用户请求提供服务。利用 Node.js 的非阻塞特性,用户请求不会在单个线程上挂起太长时间(只有在操作不是 CPU 密集型时)。

process.nextTick 和 setImmediate 有什么区别?

传递给 setImmediate 函数的回调将在事件队列上的下一次迭代中执行。

另一方面,回调传递给 process.nextTick 在下一次迭代之前以及程序中当前运行的操作完成之后执行。在应用程序启动时,开始遍历事件队列之前调用它的回调。

因此,回调 process.nextTick 总是在 setImmediate 之前调用。

什么是 yarn 和 npm?为什么要用 yarn 代替 npm 呢?

yarn 也是一个包管理器,为了解决 npm 的一些缺点。yarn 依赖 npm 注册中心为用户提供对包访问。yarn 底层结构基于 npm,如果从 npm 迁移到 yarn,项目结构和工作流不需要大改。

就像之前提到的,在某些情况下,yarn 提供了比 npm 更好的功能。与 npm 不同的是,它会缓存下载的每个包,不必重新下载。

通过校验和验证包的完整性来提供更好的安全性,保证在某个系统上运行的包在任何其他系统中的工作方式完全相同,这就是为什么选择 yarn 而不是 npm 来进行包管理。

怎么看 nodejs 可支持高并发

  1. nodejs 的单线程架构模型

我们通常认为nodejs 的是基于单线程架构模型,但nodejs 其实并不是真正的单线程架构,因为 nodejs 还有I/O线程存在(网络I/O、磁盘I/O),这些I/O线程是由更底层的 libuv 处理,这部分线程对于开发者来说是透明的。 JavaScript 代码永远运行在V8上,是单线程的。

所以从开发者的角度上来看 nodejs 是单线程的。

优势:

  • 单线程就一个线程在玩,省去了线程间切换的开销
  • 还有线程同步的问题,线程冲突的问题的也不需要担心

劣势:

  • 劣势也很明显,现在起步都是 4 核,单线程没法充分利用 cpu 的资源
  • 单线程,一旦崩溃,应用就挂掉了,大家调试脚本也知道一旦执行过程报错了,本次调试就直接结束了
  • 因为只能利用一个 cpu ,一旦 cpu 被某个计算一直占用, cpu 得不到释放,后续的请求就会一直被挂起,直接无响应了

当然这些劣势都已经有成熟的解决方案了,使用 PM2 管理进程,或者上 K8S 也可以

  1. 核心:事件循环机制

Node中的Event Loop是基于libuv实现的,而libuv是 Node 的新跨平台抽象层,libuv使用异步,事件驱动的编程方式,核心是提供i/o的事件循环和异步回调。libuv的API包含有时间,非阻塞的网络,异步文件操作,子进程等等。 Event Loop就是在libuv中实现的。

:star:最常用且特殊的 Process.nextTick()

process.nextTick()虽然它是异步API的一部分,但未在图中显示。这是因为process.nextTick()从技术上讲,它不是事件循环的一部分。

  • process.nextTick()方法将 callback 添加到next tick队列。 一旦当前事件轮询队列的任务全部完成,在next tick队列中的所有callbacks会被依次调用。

换种理解方式:

  • 当每个阶段完成后,如果存在 nextTick 队列,就会清空队列中的所有回调函数,并且优先于其他 microtask 执行。

:star:一起说说Vue的API命名nextTick

Vue 的 NextTicket

也就是说我们在设置this.msg = 'some thing'的时候,Vue并没有马上去更新DOM数据,而是将这个操作放进一个队列中;如果我们重复执行的话,队列还会进行去重操作;等待同一事件循环中的所有数据变化完成之后,会将队列中的事件拿出来处理。

这样做主要是为了提升性能,因为如果在主线程中更新DOM,循环100次就要更新100次DOM;但是如果等事件循环完成之后更新DOM,只需要更新1次。还不了解事件循环的童鞋,可以看我的另一篇文章从一道面试题来理解JS事件循环

为了在数据更新操作之后操作DOM,我们可以在数据变化之后立即使用Vue.nextTick(callback);这样回调函数会在DOM更新完成后被调用,就可以拿到最新的DOM元素了。

Maintained by · YihuiEdit on GitHub