node.js系列 6Buffer和事件循环机制
Posted lin_fightin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了node.js系列 6Buffer和事件循环机制相关的知识,希望对你有一定的参考价值。
Buffer
数据的二进制
计算机所有的内容最终都是使用二进制来表示的。而js可以直接处理其他非常直观的图片,比如字符串,但是图片这种,js一般只是告诉浏览器一个url,比如img的src。而浏览器就负责获取在这个图片,并且最终渲染出来。
前端一般可以依赖浏览器,但是对于服务器不同,我们用js,node开发服务器的时候,要处理的本地文件类型较多
Node提供了类Buffer,全局的,存储的是二进制数据
我们可以将buffer看成是一个存储二进制的数组, 【八位二进制,【】,【】】
每一项可以保存8位2进制。
八个二进制作为一个单元就是1byte(1字节) = 8 bit
1kb = 1024btye
buffer与字符串的关系
buffer相当于一个字节的数组,每一项对应一个字节,而字符串本质也是二进制,每个字符对应的是一个字节,我们也可以将字符串放入buffer去。
一个字符为一个字节,占一位,显示的是16进制
报警告了,过期了
使用Buffer.from代替new Buffer()
一个中文一般是三个字节。也可以采用不同的方式进行编码存储
使用uft16就一个中文对应两个字节
buffer.toString()可以解码//默认用utf-8
但是如果采用utf16le进行存储,解码的时候采用的是utf8所以会乱码。
解决办法:
buffer.toString(‘编码方式’)
其他创建方式
可以通过索引直接赋值,因为是展示16进制的,所以会默认转换位16进制的。
我们读取文件不指定是utf8转换时,直接读取Buffer。
读图片
有1w8k多个字节
直接读取然后写入到另一个文件去。
那我们要对图片做操作然后再写入,我们就得拿到Buffer数组,修改,然后将新的buffer数组写入即可。
使用第三方库sharp
npm install sharp
sharp可以对buffer数据做更多的处理
可以直接读文件,sharp会自动转为二进制
Buffer的创建过程
事实上我们创建buffer时,并不会频繁的向操作系统申请内存,它会默认申请一个8*1024个字节大小的内容,也就是8kb。
部分源码,创建了poolSize,然后创建createPool函数并执行
buffer.alloc
断言assertSize,判断是否为空,最后new一个FastBuffer,也就是创建新的内存,size是我们传入的大小
小结
js处理二进制数据稍显乏力,用作前端可以依赖浏览器,但是用node开发后台时就会处理二进制的数据,所以node提供了一个Buffer类,全局的,供我们来处理二进制数据,它实际上是一个数组,里面每一个都存放着一个八位的二进制,也就是一个字节,也可以使用sharp库来帮助我们处理二进制数据,读取文件,读取图片等本质都是读取二进制数据,所以我们一般要使用特定的编码如utf8来进行解码。
事件循环机制和异步IO(浏览器/node)
事件循环
事件循环可以理解成是我们编写的js代码和浏览器(node)之间的一个桥梁。
浏览器的事件循环是一个我们编写的js代码和浏览器api如setTimeout,AJAX,监听事件的一个桥梁,他们之间通过回到函数沟通
node的事件循环是一个我们编写的js代码与系统调用与file network之间的一个桥梁,也是通过回调函数进行沟通。
进程和线程
进程是计算机已经运行的程序,线程是操作系统能够运行运算调度的最小单位,
简单地说,可以认为,启动一个应用程序,就会默认启动一个或者多个进程,到那时每个进行中,都会启动一个线程来执行程序中的代码,一个进程可以有多个线程,也可以说进程是线程的容器。
再简单的理解就是操作系统类似于一个工厂,工厂有很多车间,这个车间就是进行,每个车间都至少有一个以上的工人,工人就是线程
多进程和多线程开发
比如浏览器就是多进程的,比如有渲染进程,Browser进程,GPU进程
而一个渲染进程,就有多个线程,比如js引擎线程,定时触发器线程,GUI渲染线程等等。
浏览器每个tag页就相当于一个进程。
一个浏览器20个进程。
js代码是在js引擎线程里面执行的,而js又是单线程的,这就说明只能在一个时间执行一个东西,一旦有东西耗时严重,后面就执行不了。所以有事件循环异步机制来解决这个问题。
函数调用过程
js代码执行函数的时候,是会做一个入栈,栈的话是先进后出,像我们现在在node上运行一个js文件,其实是一个模块,也是一个函数,运行的时候,将整个文件压入栈,然后执行里面的代码,遇到执行函数的时候就继续压入栈,直到所以的代码执行完毕,整个文件才会出栈。
事件循环机制
但如果有一个setTimeout的时候,setTimeout一入栈就会立马出栈,setTimeout会被执行,但是setTIMEOUT里面的函数不会被执行,setTimeout是异步的,而且不会阻塞后面的代码,为什么,因为浏览器有一个事件循环机制,setTimeout这些异步操作是会被放入事件队列里面去的
( 来自coderwhy老师的图)
浏览器会将异步的函数等保存起来,放入到事件队列,队列就是先进先出的。当函数调用栈结束后,eventool就会从队列里面准备好的函数,取出来放入到函数调用栈中。不止setTimeout,像onclick这种监听的也会放到队列去,执行顺序就是谁先进队谁先运行。
宏任务,微任务
事件队列的每个回调函数就是一个任务,而setTimeout,onclick, ajax这种就是宏任务,会放入宏任务队列(macrotask),但其实,像Promise.then(), queueMicrotask(自己定义的微任务)这种就是微任务,放入微任务队列(micortask)。
那他们的运行机制是如何,可以理解成函数调用栈同步的代码执行完毕后,开始执行微任务队列,当微任务队列执行完毕后,再执行宏任务队列,但是在执行完第一个宏任务后,还会继续查找微任务队列是否有新的任务,只有当微任务里面没有任务时,才能执行宏任务。 如果微任务中,又创建了微任务,那么还是会先执行微任务。
而async await可以看成
如
await aaa()
console.log(123213)
await后面的aaa就相当于new Promise(()=>{
aaa()这里面的代码
})
而后面的console要等到await执行完毕就相当于
new Promise((resolve)=>resolve()).then(
res=>{
console.log(123213)//在这里才能执行
}
)
就是另类的promise一样的。
以上是关于node.js系列 6Buffer和事件循环机制的主要内容,如果未能解决你的问题,请参考以下文章