2. node global&eventloop

243 阅读5分钟

node基本概念

  • Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境 (runtime)
  • 两个特点:事件驱动 非阻塞 I/O(I/O 可以理解为是文件读写)
  • node主线程还是单线程,文件读写内部还是基于多线程
  • node应用场景 处理i/o密集型(文件读写),不适合处理cpu密集型(压缩加密 和计算相关的)
  • 具体应用
    1. 服务端渲染vue react
    2. 中间层 解决跨域问题
    3. 前端会用node来实现很多工具
    4. 也可用作后端

node中的this

当用 node 执行一个文件,会把这个文件当成一个模块,这里面的 this console.log(this) // {} 默认把 this 给修改了 前端中访问变量是通过 window 属性,但是后端想访问全局需要通过 global,只有在自执行函数中 this 指向 global

console.log(this, 111); // {}

(function () {
  console.log(this, 222); // global
})();

node 中通过 global 可以访问到的属性

  • clearInterval clearTimeout...
  • queueMicrotask 微任务 基于 promise 实现的
  • clearImmediate setImmediate 自己实现的 ie 下也有这两个方法
  • node 新增的全局属性
  • Buffer node 中的二进制对象(最早的时候浏览器不能直接读写文件)服务端开发要把文件读成二进制的,数据不能存成字符串---采用 Buffer,解决了文件读写的问题
  • dirname filename
  • process
  • setImmediate

以下五个参数可以直接在文件中使用,不能通过global来获取

  • 是函数的参数
  • __filename,__dirname,exports, module, require

__filename: 代表文件所在路径(绝对路径,固定的)

__dirname: 代表文件所在目录(绝对路径,固定的)

process

  • process.platform 平台 => win32 / darwin(mac 输出),可以根据不同的平台执行不同的命令
  • process.chdir("../") 更改目录 但一般不会这样用
  • process.cwd 当前工作目录(current working directory)
    • node 在哪运行就是哪

    获取执行命令时的路径,webpack查找配置文件,就是在当前执行命令的路径下查找

  • process.env
    • 执行代码时传入环境
    • 设置环境变量: 默认就是设置成字符串

    window: set A=1 mac: export A=1

  • process.argv
    • 执行代码时传入的参数
    • console.log(process.argv); // 默认有两个 [执行node所在的exe文件, 当前执行的文件]
    • node 1.js --port 3000 --info abc 执行文件时 这样传参,会以空格分隔放到 argv 当中console.log(process.argv); // [执行node所在的exe文件, 当前执行的文件,'--port', '3000', '--info', 'abc']
    • 解析参数 库:commander/args 命令行管家
process.argv.slice(2).reduce((memo, current, index, arr) => {
  if (current.startWith('--')) {
    memo[current.slice(2)] = arr[index + 1];
  }
  return memo;
}, {});
  • process.nextTick
    • node 中自己实现的 不属于 node 中的 EventLoop
    • 是异步,优先级比 promise 更高
    • 当前执行栈的底部,在执行完同步代码之后立即执行
Promise.resolve().then(() => {
  console.log('promise');
});
process.nextTick(() => {
  console.log('nextTick');
});
// 输出 nextTick / promise

node中的事件环

  • node中自己实现了一个事件环机制
  • 新版本的node执行结果和浏览器执行的结果是一致的,底层实现的方式不太一样
  • 对于浏览器宏任务只有一个队列,对于node创造了多个宏任务队列
    • timers: 定时器(setTimeout,setInterval)回调会放到 timers 当中 ✅
    • pending callbacks: 执行延迟到下一个循环迭代的 I/O 回调 -- 不关心
    • idle,prepare:仅系统内部使用 -- 不关心
    • poll: 放 I/O 的回调 ✅
    • check: 专门放 setImmediate 回调 ✅
    • close callbacks: 一些关闭的回调
  • 执行js代码 => nextTixk => 微任务队列 => 宏任务
  • 如果在i/o操作中下一个事件队列要执行的是check,所以setsetImmediate优先级高于setTimeout
// 当前默认执行主栈代码 主栈执行完成执行定时器 但是定时器可能没有到达时间
setTimeout(() => {
  console.log('setTimeout');
}, 0);
setImmediate(() => {
  console.log('setImmediate');
});
// 这两个的输出顺序不固定, 两种情况都有可能

// 但是如果是放在文件读取callback里 就不一样
fs.readFile('./a.txt', 'utf8', (err, data) => {
  setTimeout(() => {
    console.log('setTimeout');
  }, 0);
  setImmediate(() => {
    console.log('setImmediate');
  });
}); // 输出:setImmediate / setTimeout
// 当代码执行完之后 回到poll阶段
// 会在poll阶段卡住
// 检测poll队列是不是空的,不为空会把poll中回调全部执行完毕
// poll为空会去检测有没有setImmediate,有就全部执行
// 没有setImmediate,poll就等着,等着定时器到时间
// 定时器有到时间的,就到timers,执行定时器回调,等待时间到达时可能出现新的callback, 此时也在当前阶段清空

小结: node和浏览器事件环差异--浏览器一个队列,node多个队列。执行顺序都是: 先执行代码 => (node会清空nextTick)=> 清空微任务 => 取出一个宏任务来执行。当到达poll阶段后,如果check阶段为空,而且poll里面也没有io操作,此时不会继续轮训,会等待定时器到达时间重新执行 / 或者有新的i/o操作进入到poll中

node中的模块

  • node中常用模块分为三种:
  1. 内置模块/核心模块 不需要安装直接可以使用 node自带的
  2. 文件模块 自己写的模块文件 引用的时候使用相对路径或者绝对路径
  3. 第三方模块 下载后使用 使用方式和内置模块一样
  • require就是一个读取文件的操作
  • 常用api简介
  1. fs.existsSync(./a.txt) 判断文件是否存在
  2. fs.readFileSync("./a.txt", "utf8") 同步读取文件内容
  3. path.join("a", "//b", "c", "..") 处理路径拼接 相对路径
  4. path.resolve("a", "//b", "c", "..") 也具备拼接功能,但是最终出来的结果是一个绝对路径,拼接过程中当遇到/表示的是根路径,默认以当前路径当前路径指的是 process.cwd()解析成绝对路径
  5. path.basename("a.js", ".js") // 用路径做减法
  6. path.extname("a.js") // .js取后缀名
  7. path.relative('a/b/c', '') 获取相对路径
  8. path.dirname(__filename) === __dirname
  9. let vm = require("vm"); 可以让字符串执行。vm.runInNewContext('console.log(a)')沙箱执行 实现一个全新的上下文