跳到主要内容

Nodejs

什么时候用 nodejs

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

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

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

为什么要用 nodejs

有以下几个特点: 简单强大,轻量可扩展.简单体现在node使用的是javascript,json来进行编码,人人都会;强大体现在非阻塞IO,可以适应分块传输数据,较慢的网络环境,尤其擅长高并发访问;轻量体现在node本身既是代码,又是服务器,前后端使用统一语言;可扩展体现在可以轻松应对多实例,多服务器架构,同时有海量的第三方应用组件.

node 的架构是什么样子的

主要分为三层,应用app >> V8及node内置架构 >> 操作系统. V8是node运行的环境,可以理解为node虚拟机.node内置架构又可分为三层: 核心模块(javascript实现) >> c++绑定 >> libuv + CAes + http

Nodejs 有哪些核心模块

EventEmitter,Stream,FS,Net 和全局对象

node 全局对象有哪些

process,console,Buffer

process 有哪些常用方法

process.stdin process.stdout process.stderr process.on process.env process.argv process.arch process.platform process.exit

console 有哪些常用方法

console.log console.linfo console.lerror console.warning console.time console.timeEnd console.trace console.table

node 有哪些定时功能

setTimeout clearTimeout setInterval clearInterval setImmediate clearImmediate process.nextTick

EventEmitter 做了什么

EventEmitter是node中一个实现观察者模式的类,主要功能是监听和发射消息,用于处理多模块交互问题.

Node.js 中任何对象发出的事件都是 EventEmitter 类的实例,就像 http 模块。

所有 EventEmitter 类都可以使用 eventEmitter.on() 函数将事件侦听器附加到事件。然后一旦捕捉到这样的事件,就会同步地逐个调用它的侦听器。

const events = require("events");
const eventEmitter = new events.EventEmitter();
const eventListener = function(){
console.log("event triggered");
}
eventEmitter.on("emitted", eventListener);
eventEmitter.emit("emitted");

如何实现一个 EventEmitter

主要分三步:定义一个子类,调用构造函数,继承 EventEmitter

var util = require('util');
var EventEmitter = require('events').EventEmitter;

function MyEmitter() {
EventEmitter.call(this);
} // 构造函数

util.inherits(MyEmitter, EventEmitter); // 继承

var em = new MyEmitter();
em.on('hello', function(data) {
console.log('收到事件hello的数据:', data);
}); // 接收事件,并打印到控制台
em.emit('hello', 'EventEmitter传递消息真方便!');

EventEmitter 有哪些典型应用

  • 模块间消息传递
  • 回调函数内消息传递
  • 处理流数据,因为流失在 EventEmitter 基础上实现的
  • 观察者模式发射触发机制相关应用

怎么捕获 EventEmitter 的错误事件

监听 error 事件,如果有多个 EventEmitter,也可以用 domain 来统一处理错误事件。

var domain = require('domain');
var myDomain = domain.create();
myDomain.on('error', function(err){
console.log('domain接收到的错误事件:', err);
}); // 接收事件并打印
myDomain.run(function(){
var emitter1 = new MyEmitter();
emitter1.emit('error', '错误事件来自emitter1');
emitter2 = new MyEmitter();
emitter2.emit('error', '错误事件来自emitter2');
});

EventEmitter 中的 newListenser 事件有什么用处

newListener 可以用来做事件机制的反射,特殊应用,事件管理等,当任何 on 事件添加到 EventEmitter 就会触发 newListener 事件,基于这种模式,可以做很多自定义处理。

var emitter3 = new MyEmitter();
emitter3.on('newListener', function(name, listener) {
console.log("新事件的名字:", name);
console.log("新事件的代码:", listener);
setTimeout(function(){ console.log("我是自定义延时处理机制"); }, 1000);
});
emitter3.on('hello', function(){
console.log('hello node');
});

事件循环是什么

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

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

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

node 中事件循环是什么样子

process.nextTick >> setImmidate >> setTimeout/SetInterval 看官网吧: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

流是什么

流失基于事件 EventEmitter的数据管理模式,由不同的抽象接口组成。

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

  • 可读 Readable, 作为输入数据源使用
  • 可写的 Writable, 作为输出数据源使用
  • 可读写 Duplex, 作为输出元接受被写,同时作为输入源被后面的流读出,Transform 机制和 Duplex 一样,都是双向流,同时 Transform 只需要实现一个函数 _transform(chunk, encoding,callback),而 Duplex 需要分别实现 _read(size)函数和 _write(chunk, encoding, callback) 函数
  • 先写入,再读出来

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

const fs = require("fs");
const readableStream = fs.createReadStream("test.txt");let content = "";
readableStream.on("data", (chunk) => {
content += chunk;
});
readableStream.on("end", () => {
console.log(content);
});

流有什么好处

非阻塞式数据处理提升效率,片段处理节省内存,管道处理方便可扩展等。

流有哪些典型应用

文件,网络,数据转换,音频视频

readFile 和 createReadStream 函数有什么区别

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

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

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

const fs = require("fs");
fs.readFile("test.txt", (err, content) => {
console.log(content);
});

如何实现一个 Writable Stream

分三步,构造函数 Call Writable 继承 Writable 实现 _write(chunk, encoding, callback) 函数

var Writable = require('stream').Writable;
var util = require('util');

function MyWritable(options) {
Writable.call(this, options);
} // 构造函数
util.inherits(MyWritable, Writable); // 继承自Writable
MyWritable.prototype._write = function(chunk, encoding, callback) {
console.log("被写入的数据是:", chunk.toString()); // 此处可对写入的数据进行处理
callback();
};

process.stdin.pipe(new MyWritable()); // stdin作为输入源,MyWritable作为输出源

如何处理 Nodejs 中捕获的异常

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

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

能否充分利用多核处理器

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

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

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

反应堆设计模式是什么(事件模型)

反应堆设计模式是,Node.js 将回调函数(处理程序)附加到每个 I/O 操作,然后创建请求时将处理程序提交给解复用器。

解复用器收集应用程序中发出的每个 I/O 请求,并将它们作为队列中的事件进行排队。这个队列就是我们所说的事件队列。将事件排队后,解复用器返回应用程序线程的控制。

同时,事件循环遍历事件队列中的每个事件,并调用附加的回调来处理事件响应。

这就是 Node.js 中所使用的反应堆模式。

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

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

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

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

REPL 是什么

REPL 代表 Read Eval Print Loop,是一个虚拟环境,可以在其中轻松地运行编程语言。Node.js 带有一个内置的 REPL 来运行 JavaScript 代码,类似于我们在浏览器中用来运行 JavaScript 代码的控制台。

要启动 Node.js REPL,只需在命令行上运行 node,然后写一行 JavaScript 代码,就可以在下一行看到它的输出。

process.nextTick 和 setImmediate 有什么区别

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

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

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

setImmediate(() => {
console.log("first");
})
process.nextTick(() => {
console.log("second");
})
console.log("third");

stub 是什么

测试应用程序时使用 stub,模拟给定组件或模块的行为,你可以将精力集中在要测试的代码部分。通过使用 stub 代替与测试无关的组件,不必担心外部组件会影响结果。

例如,如果正在测试的组件在预期测试的部分之前有一个文件读取操作,则可以使用 stub 来模拟该行为并返回模拟内容,而不用实际读取文件。

在 Node.js 中,我们使用像 Sinon 这样的库来实现(译者注,Sinon 在测试中替换某部分代码,减少测试项编写的复杂度 https://sinonjs.org)。

为什么在 express 中分离“应用程序”和“服务器”是一种好的做法?

通过在 Express 中分离应用程序和服务器,可以将 API 实现与网络相关配置分开。在不执行网络调用的情况下执行 API 测试,保证了更快的测试执行和更好的代码覆盖度量。

要实现这种分离,应该在单独的文件中声明 API 和 server,对应 app.js 和 server.js:

// app.js
const express = require("express");
const app = express();
app.use("/", index);
app.use("/contact", contact);
app.use("/user", user);module.exports = app;
// server.js
const http = require("http");
const app = require("/app");
app.set('port', process.env.PORT);
const http = http.createServer(app);

什么是 yarn 和 npm,为什么要用 yarn 代替 npm

npm 是与 Node.js 自带的默认包管理器,它有一个大型的公共库和私有库,存储在 npm registry 的数据库中(译者注,官方默认中心库 http://registry.npmjs.org/,国内淘宝镜像 http://registry.npm.taobao.org/),用户可以通过 npm 命令行访问该数据库。在 npm 的帮助下,用户可以轻松管理项目中的依赖项。

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

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

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

参考