nodejs知识点总结

孤者浪人 提交于 2019-12-05 14:40:04

Event Loop的理解

JavaScript 是单线程的,当主线程遇到耗时的I/O操作,就会把把这些操作尽量转移给操作系统来执行,而操作系统是多线程(同理,浏览器中的js也是单线程的,只不过浏览器是多线程的)。nodejs的底层libuv使用线程池来处理这些异步的任务,当任务处理完成后,操作系统会通知nodejs,nodejs就会把对应的回调函数添加到poll轮训队列中,nodejs处理完主线程的任务后,就会从轮训队列中执行对应的回调函数。

event loop 执行完毕的情况:

  1. 队列的操作全被执行完了
  2. 执行的回调数目到达指定的最大值

nodejs轮询阶段示意图:

  • timers 执行: setTimeout 和 setInterval 的回调函数
  • I/O callbacks: 不在 timers 阶段、close callbacks 阶段和 check 阶段这三个阶段执行的回调,都由此阶段负责,这几乎包含了所有回调函数
  • idle, prepare: event loop内部阶段,我们暂不需要了解
  • poll: 获取新的 I/O 事件
  • check: 执行 setImmediate
  • close callback: 执行关闭事件的回调函数,如 mongoose.on('close', fn) 里的 fn

异步相关的理解

promise

  • 不管是resolve还是reject,Promise.prototype.then()和Promise.prototype.catch()方法会返回一个全新的Promise对象(finally()不会返回promise),因此它们之间可以进行链式调用。一旦一个Promise调用环节出现了错误,被catch捕获,则后续的promise则不会被调用。
  • promise的执行
new Promsie((resolve,reject)=>{
    console.log('1');
    setTimeout(()=>{
        resolve('2');
    },2000);
    console.log('3')
}).then(res=>{
    console.log(res);
});

执行结果:
1
3
一秒钟后
2

解释:promise在创建后,不管有没有调用其then方法,promise都会执行,并保存执行结果,此后不管再调用多少次then方法,其结果不变。
复制代码
  • promise.all()实现请求超时处理 promise.all()可以来实现并发操作,这时可以定义一个五秒钟后必定失败的promise,这样就可以实现请求超时处理了
  • Promise的构造函数以及then()中执行的函数都可以认为是在try...catch块中运行,因此即便使用了throw,程序本身也不会因为抛出异常而终止。

generator

  • 形式上:

    Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield语句,定义不同的内部状态

  • 调用方式:

    enerator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,我们可以通过调用 next 方法,使得指针移向下一个状态

  • 通过next函数传值:

    这个功能有很重要的语法意义。Generator 函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。通过next方法的参数,就有办法在 Generator 函数开始运行之后,继续向函数体内部注入值。也就是说,可以在 Generator 函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。这个用处在其用来实现 async/await 至关重要。

async/await

  • generator函数自动执行的语法糖
  • 有配合promise和不配合promise两种实现async/await的方法

process常见考点

1. setImmediate、setTimeOut、setInterval、process.nextTick的区别

在轮询阶段:

  • 在整个轮询的开始执行process.nextTick
  • 然后再执行setTimeOut、setInterval
  • 再执行其他的回调函数
  • 最后执行setImmediate

process.nextTick()的理解

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

nextTick更加有效率。事件轮询随后的ticks调用,会在任何I/O事件(包括定时器)之前运行。

每次事件轮询后,在额外的I/O执行前,next tick队列都会优先执行。 递归调用nextTick callbacks 会阻塞任何I/O操作,就像一个while(true); 循环一样。

nodejs如何利用多核

  • cluster模块

  • child_process模块

  • 利用pm2,fork或者cluster模式

Buffer

nodejs的重点就是文件(fs)和网络部分(http),但是这两部分都是依赖stream,而stream都使用了Buffer模块。

概念:

  • TypedArray:读取或者操作二进制数据流的机制
  • Buffer:Buffer 类以一种更优化和适用于 NodeJS 操作的方式实现了 Unit8Array API。总而言之,Buffer 类是用来处理二进制数据,因为太常用了,所以直接放在了全局变量里,使用的时候无需 require。

Buffer 类的实例类似于整型数组,不过缓冲区的大小在创建时确定,不能调整。Buffer 对象不同之处在于它不经 V8 的内存分配机制,Buffer 是一个 JavaScript 和 C++ 结合的模块,内存由 C++ 申请,JavaScript 分配。

使用:

  • Buffer.from()
  • Buffer.alloc()
  • Buffer.allocUnsafe()

详细使用请参考:NodeJS stream 一:Buffer

stream

参考:

概念:

  • 从术语上讲流是对输入输出设备的抽象

  • 从程序角度而言流是有方向的数据,按照流动方向可以分为三种流:

    • 设备流向程序:readable(readable
    • 程序流向设备:writable
    • 双向:duplex、transform
  • 按照 Unix 的哲学:一切皆文件,在 NodeJS 中对文件的处理多数使用流来完成

    • 普通文件
    • 设备文件(stdin、stdout)
    • 网络文件(http、net)
  • 在 NodeJS 中所有的 Stream 都是 EventEmitter 的实例

使用:

const fs = require('fs');
const FILEPATH = '...';

const rs = fs.createReadStream(FILEPATH);
const ws = fs.createWriteStream(DEST);

rs.pipe(ws);
复制代码

数据必须是从上游 pipe 到下游,也就是从一个 readable 流 pipe 到 writable 流。

流的两种工作方式:

  • 流动模式:数据由底层系统读出,并尽可能快地提供给应用程序
  • 暂停模式:必须显示地调用 read() 方法来读取若干数据块

流在默认状态下是处于暂停模式的,也就是需要程序显式的调用 read() 方法,可我们的例子中并没有调用就可以得到数据,因为我们的流通过 pipe() 方法切换成了流动模式,这样我们的 _read() 方法会自动被反复调用,直到数据读取完毕,所以我们每次 _read() 方法里面只需要读取一次数据即可。

流动模式和暂停模式切换:

  • 暂停模式到流动模式:

    • 通过添加 data 事件监听器来启动数据监听
    • 调用 resume() 方法启动数据流
    • 调用 pipe() 方法将数据转接到另一个 可写流
  • 流动模式切换为暂停模式:

    • 在流没有 pipe() 时,调用 pause() 方法可以将流暂停
    • pipe() 时,需要移除所有 data 事件的监听,再调用 unpipe() 方法

流的事件:

  • data:触发流为流动模式
  • end:数据处理完成后会触发一个 end 事件
  • error:数据处理过程中出现了错误会触发 error 事件
  • readable:NodeJS 为我们提供了一个 readable 的事件,事件在可读流准备好数据的时候触发,也就是先监听这个事件,收到通知又数据了我们再去读取就好了

开始使用流动模式的时候我经常会担心一个问题,上面代码中可读流在创建好的时候就生产数据了,那么会不会在我们绑定 readable 事件之前就生产了某些数据,触发了 readable 事件,我们还没有绑定,这样不是极端情况下会造成开头数据的丢失嘛

可事实并不会,按照 NodeJS event loop 我们创建流和调用事件监听在一个事件队列里面,儿生产数据由于涉及到异步操作,已经处于了下一个事件队列,我们监听事件再慢也会比数据生产块,数据不会丢失。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!