Node 核心和 Node eventLoop
2020-09-01 本文已影响0人
育儿与心理
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 是多个宏任务队列
- 执行顺序是一样的