关于前端:深入学习Nodejs1-基础概念

34次阅读

共计 3810 个字符,预计需要花费 10 分钟才能阅读完成。

前言

当初 Node.js 对于前端越来越重要,笔者当初也感触到了学好 Node.js 能够极大的晋升前端竞争力。之前也或多或少的接触过,然而没有一个零碎的学习过程,所以笔者接下来打算零碎的去学习 Node.js,同时心愿建设一个从零到一学习 Node 的一个博客。

心愿我的学习教训能够帮忙到大家。

Node.js 是什么

都 2020 年了,大家对 Node 必定都有了初步的理解,然而我还是想先介绍一下什么是 Node

Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境(runtime)。

Node 不是一门语言是让 js 运行在后端的运行时, 并且不包含 javascript 选集, 因为在服务端中不蕴含 DOM 和 BOM。Node 也提供了一些新的模块例如 http,fs 模块等。

Node.js 应用了事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。并且 Node.js 的包管理器 npm,是寰球最大的开源库生态系统。

为什么应用要学习 Node.js

(1)首先 Node 在解决高并发,IO 密集场景有显著劣势。

高并发是指同一时间并发拜访服务器

IO 即输入输出,是指文件读写,网络操作,数据库操作等。绝对的是 CPU 密集型,意思是逻辑解决运输频繁,个别指解密、加密、压缩、解压等操作。

(2)其次,当初前端开发越来越离不开 Node。日常工作中的 webpack、cli 脚手架工具等都是由 Node 编写。前端学会 Node 会极大的减少本人的话语权,进步本人的竞争力。

异步非阻塞 IO

传统语言例如大部分都是采纳同步阻塞的形式,即当我读取某个文件的内容时,我必须期待所有文件内容读取结束能力进行上面的内容,因而整个线程都将进行执行,造成了阻塞,极大水平的升高了执行效率。

因为 Node.js 采纳异步非阻塞的模型,因而当我对读取某个文件时,将立刻执行上面的代码,当文件读取结束,将处理结果放入咱们的回调函数当中,减少了执行效率。

阻塞模式下,一个线程只能解决一个 IO,如果想并发解决多个工作只能开启多个线程。而非阻塞模式下一个线程就能够解决多个 IO,CPU 的利用率能够达到最大。

单线程

Node.js 采纳单线程模式,这也良好的实用了单线程非阻塞的模式。当 Node.js 须要解决 IO 时,会将工作交给外部的 libuv,libuv 解决实现后,通过 IO 事件驱动机制,将后果返回给 Node.js 的线程。

事件驱动

每一次 IO 操作实现都会触发相应的事件,因为 Node.js 是单线程,所以同一时刻只能解决一个工作,那多余的工作只能放到队列中期待被调用。而为了解决这一过程 Node.js 采纳了事件循环的策略,相似于浏览器的事件循环,但有很多出入。

事件循环

当 Node.js 启动后,它会初始化事件循环,解决已提供的输出脚本,它可能会调用一些异步的 API、调度定时器,或者调用 process.nextTick(),而后开始处理事件循环。

上面是一张来自官网的事件循环的形容图。

事件循环官网介绍的十分分明,我这里把官网的内容搬运过去。

留神:每个框被称为事件循环机制的一个阶段。

每个阶段都有一个 FIFO 队列来执行回调。尽管每个阶段都是非凡的,但通常状况下,当事件循环进入给定的阶段时,它将执行特定于该阶段的任何操作,而后执行该阶段队列中的回调,直到队列用尽或最大回调数已执行。当该队列已用尽或达到回调限度,事件循环将挪动到下一阶段,等等。

因为这些操作中的任何一个都可能调度 更多的 操作和由内核排列在 轮询 阶段被解决的新事件,且在解决轮询中的事件时,轮询事件能够排队。因而,长时间运行的回调能够容许轮询阶段运行长于计时器的阈值工夫。

阶段概述

  • 定时器:本阶段执行曾经被 setTimeout() 和 setInterval() 的调度回调函数。
  • 待定回调(pending callbacks):执行提早到下一个循环迭代的 I/O 回调。
  • idle, prepare:仅零碎外部应用。
  • 轮询(poll):检索新的 I/O 事件; 执行与 I/O 相干的回调(简直所有状况下,除了敞开的回调函数,那些由计时器和 setImmediate() 调度的之外),其余状况 node 将在适当的时候在此阻塞。
  • 检测(check):setImmediate() 回调函数在这里执行。
  • 敞开的回调函数(close callbacks):一些敞开的回调函数,如:socket.on(‘close’, …)。

在每次运行的事件循环之间,Node.js 查看它是否在期待任何异步 I/O 或计时器,如果没有的话,则齐全敞开。

阶段的具体概述

定时器

计时器指定 能够执行所提供回调 的 阈值,而不是用户心愿其执行的确切工夫。在指定的一段时间距离后,计时器回调将被尽可能早地运行。然而,操作系统调度或其它正在运行的回调可能会提早它们。

留神 轮询 阶段 管制何时定时器执行。

例如,假如您调度了一个在 100 毫秒后超时的定时器,而后您的脚本开始异步读取会消耗 95 毫秒的文件:

const fs = require('fs');

function someAsyncOperation(callback) {
  // Assume this takes 95ms to complete
  fs.readFile('/path/to/file', callback);
}

const timeoutScheduled = Date.now();

setTimeout(() => {const delay = Date.now() - timeoutScheduled;

  console.log(`${delay}ms have passed since I was scheduled`);
}, 100);

// do someAsyncOperation which takes 95 ms to complete
someAsyncOperation(() => {const startCallback = Date.now();

  // do something that will take 10ms...
  while (Date.now() - startCallback < 10) {// do nothing}
});

当事件循环进入 轮询 阶段时,它有一个空队列(此时 fs.readFile() 尚未实现),因而它将期待剩下的毫秒数,直到达到最快的一个计时器阈值为止。当它期待 95 毫秒过后时,fs.readFile() 实现读取文件,它的那个须要 10 毫秒能力实现的回调,将被增加到 轮询 队列中并执行。当回调实现时,队列中不再有回调,因而事件循环机制将查看最快达到阈值的计时器,而后将回到 计时器 阶段,以执行定时器的回调。在本示例中,您将看到调度计时器到它的回调被执行之间的总提早将为 105 毫秒。

留神:为了避免 轮询 阶段饿死事件循环,libuv(实现 Node.js 事件循环和平台的所有异步行为的 C 函数库),在进行轮询以取得更多事件之前,还有一个硬性最大值(依赖于零碎)。

挂起的回调函数(pending callbacks)

此阶段对某些零碎操作(如 TCP 谬误类型)执行回调。例如,如果 TCP 套接字在尝试连贯时接管到 ECONNREFUSED,则某些 *nix 的零碎心愿期待报告谬误。这将被排队以在 挂起的回调 阶段执行。

轮询

轮询 阶段有两个重要的性能:

  1. 计算应该阻塞和轮询 I/O 的工夫。
  2. 而后,解决 轮询 队列里的事件。

当事件循环进入 轮询 阶段且 没有被调度的计时器时,将产生以下两种状况之一:

  • 如果 轮询 队列 不是空的,事件循环将循环拜访回调队列并同步执行它们,直到队列已用尽,或者达到了与零碎相干的硬性限度。
  • 如果 轮询 队列 是空的,还有两件事产生:
    • 如果脚本被 setImmediate() 调度,则事件循环将完结 轮询 阶段,并持续 查看 阶段以执行那些被调度的脚本。
    • 如果脚本 未被 setImmediate() 调度,则事件循环将期待回调被增加到队列中,而后立刻执行。

一旦 轮询 队列为空,事件循环将查看 _已达到工夫阈值的计时器_。如果一个或多个计时器已准备就绪,则事件循环将绕回计时器阶段以执行这些计时器的回调

查看阶段

此阶段容许人员在轮询阶段实现后立刻执行回调。如果轮询阶段变为闲暇状态,并且脚本应用 setImmediate() 后被排列在队列中,则事件循环可能持续到 查看 阶段而不是期待。

setImmediate() 实际上是一个在事件循环的独自阶段运行的非凡计时器。它应用一个 libuv API 来安顿回调在 轮询 阶段实现后执行。

通常,在执行代码时,事件循环最终会命中轮询阶段,在那期待传入连贯、申请等。然而,如果回调已应用 setImmediate()调度过,并且轮询阶段变为闲暇状态,则它将完结此阶段,并持续到查看阶段而不是持续期待轮询事件

以上事件循环过程的内容来自 Node.js 官网

微工作

相熟浏览器事件循环的都晓得,每次宏工作执行结束都会执行微工作队列里的工作。同样 Node.js 也有微工作,只不过它有两个微工作队列,别离是 process.nextTick 与 Promise。

每个阶段执行结束都会去执行 process.nextTick 队列内的工作与 promise 队列内的工作。process.nextTick 的优先级高于 promise。

间隔阐明

Promise.resolve().then(() => console.log('promise'))

process.nextTick(() => console.log('nextTick'))

// 输入后果

// nextTick
// promise

以上就是 Node.js 的一些根底概念。

正文完
 0