阅读 1686

Node 核心和 Node eventLoop

Node 核心和 Node eventLoop

Node 是什么

  • Node.js 是一个开源与跨平台的 JavaScript 运行时环境;
  • Node.js 在浏览器外运行 V8 JavaScript 引擎(Google Chrome 的内核)
  • Node.js 应用程序运行于单个进程中,无需为每个请求创建新的线程
  • Node.js 在其标准库中提供了一组异步的 I/O 原生功能(用以防止 JavaScript 代码被阻塞),并且 Node.js 中的库通常是使用非阻塞的范式编写的(从而使阻塞行为成为例外而不是规范)
  • Node.js 执行 I/O 操作时(例如从网络读取、访问数据库或文件系统),Node.js 会在响应返回时恢复操作,而不是阻塞线程并浪费 CPU 循环等待
  • Node.js 又基于js核心 (ecmascript) 系统级的api 文件操作,网络编程 实现自己的 web 服务

Node 解决了什么问题

Web服务器,瓶颈在于用户的并发量 (多线程 同步) 只要多个人需要操作同一个资源必须通过锁

  • java php 用户访问服务器,每个客户连接服务器时 都会产生一个新的线程,一个线程大约占用 2mb 内存,8g 内存
  • node 不会创建新的线程,就发射一个事件
  • 前后端分离,写一些工具库 webpack,cli

node 比较适合web应用场景 返回文件 文件读写

Node核心特点

  • 事件驱动 Node.js 的 api 是基于事件的 异步的
  • Node.js 采用的是单线程 进程(主线程)node.js 可以开启多个进程
  • 不适合 cpu 密集型 i/o密集型

同步异步 阻塞非阻塞(针对的点不一样的)

  • 同步和异步,关注的是消息通知的机制 readFile
    • 同步体现在,在等待一件事情的处理结果时,对方是否提供通知服务,如果对方不提供通知服务,则为同步
    • 异步体现在,在等待一件事情的处理结果时,对方是否提供通知服务,如果对方提供通知服务,则为异步。
  • 阻塞和非阻塞 程序等待消息结果的状态
    • 阻塞,在等待一件事情的处理结果时,你是否还去干点其他的事情,如果不去,则为阻塞;
    • 非阻塞,在等待一件事情的处理结果时,你是否还去干点其他的事情,如果去了,则为非阻塞;

Node 中的全局对象

this 和 module.exports 是 === 的

  console.log(this); // {} this 不是 global
  console.log(this === global); // false
  console.log(this === module.exports); // true
复制代码

arguments

  console.log(arguments);
  // [Arguments] {
  //   '0': {},
  //   '1': [Function: require] {
  //     resolve: [Function: resolve] { paths: [Function: paths] },
  //     main: Module {
  //       id: '.',
  //       path: '/Users/hiraku/myself/architecture-product/node/4.global',
  //       exports: {},
  //       parent: null,
  //       filename: '/Users/hiraku/myself/architecture-product/node/4.global/index.js',
  //       loaded: false,
  //       children: [],
  //       paths: [Array]
  //     },
  //     extensions: [Object: null prototype] {
  //       '.js': [Function],
  //       '.json': [Function],
  //       '.node': [Function],
  //       '.mjs': [Function]
  //     },
  //     cache: [Object: null prototype] {
  //       '/Users/hiraku/myself/architecture-product/node/4.global/index.js': [Module]
  //     }
  //   },
  //   '2': Module {
  //     id: '.',
  //     path: '/Users/hiraku/myself/architecture-product/node/4.global',
  //     exports: {},
  //     parent: null,
  //     filename: '/Users/hiraku/myself/architecture-product/node/4.global/index.js',
  //     loaded: false,
  //     children: [],
  //     paths: [
  //       '/Users/hiraku/myself/architecture-product/node/4.global/node_modules',
  //       '/Users/hiraku/myself/architecture-product/node/node_modules',
  //       '/Users/hiraku/myself/architecture-product/node_modules',
  //       '/Users/hiraku/myself/node_modules',
  //       '/Users/hiraku/node_modules',
  //       '/Users/node_modules',
  //       '/node_modules'
  //     ]
  //   },
  //   '3': '/Users/hiraku/myself/architecture-product/node/4.global/index.js',
  //   '4': '/Users/hiraku/myself/architecture-product/node/4.global'
  // }
复制代码

global key

  console.log(Object.keys(global));
  // [
  //   'global',
  //   'clearInterval',
  //   'clearTimeout',
  //   'setInterval',
  //   'setTimeout',
  //   'queueMicrotask',
  //   'clearImmediate',
  //   'setImmediate'
  // ]
复制代码

process 进程

  console.log(Object.keys(process));
  // ['version','arch','platform','release','_rawDebug','moduleLoadList','binding','_linkedBinding','_events','_eventsCount','_maxListeners','domain','_exiting','config','abort','umask','chdir','cwd','_debugProcess','_debugEnd','_startProfilerIdleNotifier','_stopProfilerIdleNotifier','dlopen','uptime','_getActiveRequests','_getActiveHandles','reallyExit','_kill','hrtime','cpuUsage','resourceUsage','memoryUsage','kill','exit','getuid','geteuid','getgid','getegid','getgroups','initgroups','setgroups','setegid','seteuid','setgid','setuid','stdout','stderr','stdin','openStdin','allowedNodeEnvironmentFlags','assert','features','_fatalException','setUncaughtExceptionCaptureCallback','hasUncaughtExceptionCaptureCallback','emitWarning','nextTick','_tickCallback','env','title','argv','execArgv','pid','ppid','execPath','debugPort','argv0','_preload_modules','mainModule'
复制代码
  • process.argv
  // node index.js --port 3000
  console.log(process.argv);
  // 1. 当前 node 的执行命令文件
  // 2. 当前执行的文件是谁 node + 文件执行时 可以传递参数 这些参数会放到 数组的第三项
  // 3. 解析用户传递的参数
  // [ '/Users/hiraku/.nvm/versions/node/v11.10.0/bin/node',
  //   '/Users/hiraku/myself/architecture-product/node/global/index.js',
  //   '--port',
  //   '3000' ]
  const argvObj = process.argv.slice(2).reduce((memo, current, index, arr) => {
    if(current.startsWith('--')) {
      memo[current.slice(2)] = arr[index + 1];
    }
    return memo;
  }, {});
  console.log(argvObj);
复制代码
  • process.platform 进程运行的平台
  console.log(process.platform, 'platform'); // darwin
  // win32 darwin
复制代码
  • commander 的使用
  const cmd = require('commander');
  cmd.name('node global');
  cmd.usage('index.js');
  cmd.version('1.0.0');
  cmd.option('-p,--port <v>', 'please set you prot ');
  cmd.option('-c,--config <v>', 'please set  you config file ');
  cmd.command('create').action(() => { // 运行时会执行此方法
      console.log('创建项目');
  });
  cmd.on('--help', function () {
      console.log('\r\nRun command')
      console.log('\r\n  node global -p 3000')
  });
  const r = cmd.parse(process.argv);
  console.log(r);
  console.log(process.env);
  console.log(process.cwd());

  // Usage: node global index.js

  // Options:
  //   -V, --version    output the version number
  //   -p,--port <v>    please set you prot
  //   -c,--config <v>  please set  you config file
  //   -h, --help       display help for command

  // Commands:
  //   create
  //   help [command]   display help for command

  // Run command

  //   node global -p 3000
复制代码
  • process.cwd 当前工作目录,如 webpack 找配置文件,当前工作目录下查找
  console.log(process.cwd()); // /Users/hiraku/myself/architecture-product/node/4.global
复制代码
  • 在当前命令行窗口下设置环境变量
// window set命令 export 命令  => cross-env
console.log(process.env) // 当前进程的环境变量 会用他来区分各种环境
// cross-env env=development && node xxxx
复制代码

Node 中的实现的微任务

  • node 中实现的微任务 他的优先级比 promise 还要高
  • nextTick 和 promise 是两个队列,所以会先清空 nextTick 队列
  • node 的事件环 在 nodeV10 版本之后,统一执行效果和浏览器一致,每个宏任务执行完都会清空微任务
  • 老版本是每个队列清空后清空微任务
  process.nextTick(() => {
    console.log(1);
    process.nextTick(() => {
      console.log(2);
      process.nextTick(() => {
        console.log(3);
      });
    });
  });
  Promise.resolve().then(() => {
    console.log('promise')
  });

  // 1 2 3 promise
复制代码
  • 如果是 setImmediate 和 setTimeout 在默认环境下执行会受性能影响
  setImmediate(() => { // 立即
    console.log('setImmediate'); // node 中的宏任务
  });
  setTimeout(() => {
    console.log('setTimeout');
  }, 0);
  // setTimeout
  // setImmediate
复制代码
  • setImmediate nextTick
  setImmediate(() => {
    console.log('setImmediate1');
    process.nextTick(() => {
      Promise.resolve().then(() => {
        console.log('promise1');
      });
    });
  });
  setImmediate(() => {
    console.log('setImmediate2');
    Promise.resolve().then(() => {
      console.log('promise2');
    });
    process.nextTick(() => {
      console.log('nextTick2');
    });
  });
  process.nextTick(() => {
    console.log('nextTick1');
  });

  // nextTick1
  // setImmediate1
  // promise1
  // setImmediate2
  // nextTick2
  // promise2
复制代码
  • 总结
    • 默认当主代码执行完毕后会进入到事件环
    • 会先看当前定时器是否到达时间,如果到达时间会执行 定时器的回调
    • poll 阶段会执行 i/o 操作的回调,如果没有 i/o 看一下有没有 setImmediate,如果有会进入到 check 阶段
    • 如果没有 要检查是否有定时器如果没定时器也没有,i/o操作则结束循环
    • 如果有定时器,定时器到达时间后,会返回 timer 阶段执行定时器的回调
    • 每一个宏任务执行完毕后都会清空微任务

  • 定时器:本阶段执行已经被 setTimeout() 和 setInterval() 的调度回调函数。

  • 待定回调:执行延迟到下一个循环迭代的 I/O 回调。

  • idle, prepare:仅系统内部使用。

  • 轮询:检索新的 I/O 事件;执行与 I/O 相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和 * setImmediate() 调度的之外),其余情况 node 将在适当的时候在此阻塞。

  • 检测:setImmediate() 回调函数在这里执行。

  • 关闭的回调函数:一些关闭的回调函数,如:socket.on('close', ...)。

  • 宏任务微任务分类

    • Vue.nextTick 混合型任务,可能是微任务也可能是宏任务
    • 微任务 promise.then、mutationObserver、process.nextTick
    • 宏任务 script 标签、ui 渲染 、MessageChannel(浏览器) 、ajax、event 事件、setTimeout
    • setImmediate requestFrameAnimation
    • 浏览器是一个宏任务队列 node 是多个宏任务队列
    • 执行顺序是一样的
文章分类
代码人生
文章标签