今日分享Node 核心和 Node eventLoop

Posted PDraw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了今日分享Node 核心和 Node eventLoop相关的知识,希望对你有一定的参考价值。

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 是多个宏任务队列

    • 执行顺序是一样的


以上是关于今日分享Node 核心和 Node eventLoop的主要内容,如果未能解决你的问题,请参考以下文章

第1523期Node.js 中的依赖管理

第1496期新手向之Vue.js + Node.js(koa) 合体指南

第1768期Node.js 在微医的应用场景及实践

今日好书丨《Node.js:来一打 C++ 扩展》

浏览器和Node不同的事件循环(Event Loop)

第1371期Node.js 前端开发指南