今日分享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的主要内容,如果未能解决你的问题,请参考以下文章