node深入学习 eventloop
Posted lin-fighting
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了node深入学习 eventloop相关的知识,希望对你有一定的参考价值。
多线程
优点:同时处理多个请求,适合cpu密集型运算
缺点:如果多个线程操作同一个资源得上锁。多线程并不是一起去干一些事情,而是靠切换上下文(浪费一些性能)
单线程
node的主线程是单线程,不需要开启多个线程,节省资源,不适合做大量的cpu操作。但是node提供了开启子进程,可以将比较大的操作放入子进程去运行。
同步异步和阻塞非阻塞
阻塞非阻塞是相对于操作方的,操作放调用了同步方法,这时候就会阻塞。同步异步是相对于调用的方法。比如调用readFile就是异步的。
异步非阻塞:调用异步的方法,操作方不会被阻塞。
event loop
因为node的主线程是单线程,所以node也实现了自己的event loop。
- 1 代码会交给v8引擎进行处理
- 2 代码中可能会调用nodeApi,node会交给libuv库处理
- 3 libuv通过阻塞i/o和多线程实现了异步io。
- 4 通过事件驱动的方式,将结果放入事件队列中,最终交给我们的应用。
node使用
默认执行一个文件的时候,在命令行运行node index.js文件的时候,他会把整个文件当成一个模块,默认把this修改了。
在全局下指向的就是global。
node新增的全局属性
- buffer node中的二进制对象(最早的时候浏览器不能直接读写文件)
- process 进程
process中的一些属性:
- platform 当前执行环境的平台(如window(win32),mac(darwin))
- chidir,以更改当前工作的目录
- env 执行代码的时候可以传入一些环境变量
- argv 执行代码的时候可以传入的参数
可以通过传入参数获取。第一二个值是固定的,
[执行node所在的exe文件,当前的执行文件,…其他参数],用户脚手架启动项目的时候传入一些参数,然后在配置文件中获取这些值。
解析可以用到commander库(命令行管家)
- nextTick
下面祥讲 - cwd (current working directory)当前工作的目录,比如webpack会自动擦护照运行webpack的目录下查找webpack.config.js文件。
- __dirname(当前执行时的目录,绝对路劲) __filename(当前执行时的文件的绝对路劲)
- setImmediate
nextTick与node的eventloop
node中自己实现的,不属于node的eventloop。优先级比Promise更高。nextTick算是微任务,他是当前栈中同步代码执行完毕后立即执行的。因为Node里也有一些异步操作,所以node实现了自己的eventLoop。
而node中的异步队列不比浏览器,node有很多类型的宏任务队列。
这是官网的案例。
一共有六种。
- 第一种是setTImeout等到时间就放入这个队列。
第二种就是一些回调函数,还有第四种io任务非常多,导致只能下一个循环执行,就会放入这里。
第三种是内部使用的
第四种是与io相关的回调 (poll, 比如readFIle)。
第五种是setImmediate的回调函数
第六种是一些关闭的回调函数。
我们重点需要关注第一种,第四种,第五种。
poll主要是放Io的回调,必须对文件的读写这些readFile等。
第五种check放一些setImmediate的回调。 - 代码是从上往下执行的,从第一种走到第六种,走完之后继续走,是个死循环。
- 但是有个问题,比如setTimeout按道理是在setImmediate前执行,但是有时候会setImmediate先执行。是因为当前默认执行主栈代码,主栈代码执行完毕后要执行定时器,但是定时器可能还没到时间,所以就先往下走,所以setImmediate可能会先执行。
有个特殊情况。就是
当执行io操作的时候,如上,主栈执行完代码之后,会走eventloop,此时setTImeout还没准备好,会直接走到第四种,poll,poll会等待,等待setTimeout执行完之后再回到第一种往下执行,而等待的这段时间,poll会检测以后没有setImmediate要执行,有的话会先执行第五个队列check的东西。
所以会出现这种情况。等待完毕后会往回去执行第一个timer队列里面的东西。
- 浏览器的特点是,先执行栈中代码,清空后为执行微任务,然后渲染页面,再从宏任务队列中取出一个来执行。
- node是先执行栈代码,执行完毕后,清空当前的微任务队列,会进入事件环中(evetloop),拿出一个来执行,执行完毕后再次清空当前微任务队列。(以前是一个队列! 清空完,再清空微任务,现在是一个执行完,就清空微任务,为的就是跟浏览器表现一致)
global的其他重要属性
global上可以直接访问的属性叫全局属性,而requrei,exports,moudel也可以直接访问,但是不在global。因为每个文件都是一个模块,就好比自执行函数。如
(function(rqeuire, exports, module))(require,exports,module)
他是作为入参传入,然后在这个模块就可以使用这些。而global上没有。
模块化
为什么要有模块化?
- 一开始是为了解决命名冲突问题
- 最开始是用对象,单例模式,但是不能完全解决这些问题
- 然后就是自执行函数,配合文件拆问的方式。但是会有请求问题,依赖问题,(amd cmd)。所以才有define([‘jqury’,‘vue’],()=>)这种前置依赖,后来也逐渐被淘汰。
- umd,兼容amd+cmd+commonjs,但是不支持esmodule
- commonjs规范(一个文件就是一个模块,使用就用require,导出就module.exports,他只是一个规范) esmoudle(es的规范,使用就是import from ,到处就是export)
- commonjs是基于文件的读写的,是同步的,而且不能在编译时确定,只有在运行的时候才会报错。)
- esmodule支持静态编译,(正常每次import一个模块,会发请求,是异步操作,但现在都是靠webpack编译,不会发请求,而是编译成require这些。)
- 这也是为什么import不能在if里面编写。import是静态导入,因为总不能在if(true)import 'xx’就去请求,卡在这里,只能放在顶部,这样webpack就会去编译,而放入条件判断中,因为条件判断必须要动态执行才知道结果,所以不可以这样。但是commonjs可以。因为require是同步的,后面es7又出了一个import()就可以在if里面编写。
以上是关于node深入学习 eventloop的主要内容,如果未能解决你的问题,请参考以下文章