浅谈Node IO操作
学习Node就绕不开异步IO, 异步IO又与事件循环息息相关, 而关于这一块一直没有仔细去了解整理过, 刚好最近在做项目的时候, 有了一些思考就记录了下来, 希望能尽量将这一块的知识整理清楚, 如有错误, 请指点轻喷~~
同步异步 & 阻塞非阻塞
查阅资料的时候, 发现很多人都对异步和非阻塞的概念有点混淆, 其实两者是完全不同的, 同步异步指的是行为即两者之间的关系 , 而阻塞非阻塞指的是状态即某一方。
以前端请求为一个例子,下面的代码很多人都应该写过
$.ajax(url).succedd(() => { ...... // to do something })
同步异步
如果是同步的话, 那么应该是客户端发起请求后, 一直等到serve处理请求完成后才返回继续执行后续的逻辑, 这样客户端和服务端之间就保持了同步的状态
。
如果是异步的话, 那么应该是客户端发起请求后, 立即返回, 而请求可能还没有到达服务端或者请求正在处理, 当然在异步情况下, 客户端通常会注册事件来处理请求完成后的情况, 如上面的succeed函数。
阻塞非阻塞
首先需要明白一个概念, Js是单线程, 但是浏览器并不是, 事实上你的请求是浏览器的另一个线程在跑。
如果是阻塞的话, 那么该线程就会一直等到这个请求完成之后才能被释放用于其他请求。
如果是非阻塞的话, 那么该线程就可以发起请求后而不用等请求完成继续做其他事情。
IO是什么?
I/O(英语:Input/Output),即输入/输出,通常指数据在内部存储器和外部存储器或其他周边设备之间的输入和输出。
异步IO操作
文件读取的方式是一次性全部读取,当文件过大的时候,一次性读取不仅缓慢,而且影响用户体验,那么怎么实现分步读取呢,
这就得使用到异步IO的操作,像水流一样流出一段取得一段。
具体实现:
我们创建一个文件读取流,先上代码
var fs = require("fs"); var data = ""; //声明一个空字符串来存读取的数据 var rs = fs.createReadStream("a.txt"); rs.setEncoding("utf-8"); //监听当有数据流入的时候 rs.on("data",function(chunc){ data += chunc; //将读取的数据拼接到data上。 console.log("..."); //读的过程中,我们打印三个点。 }); rs.on("end",function(){ console.log("没有数据了") });
我们将 a.txt中的内容增加,以让读取时间变长,
代码中,创建main3.js写入上面的代码,使用 reateReadStream创建读取流对象,在对象上使用on监听“data”读取数据的事件,每读取一段数据,就会触发这个事件,当读取完毕, 就会触发“end”事件。
执行main3.js,我们就可以看到下面打印的结果,从打印的多行"..."中,我们就可以看出,读取了多次才读完。
将读取到的数据,慢慢的写入 b.txt中
修改mai3.js中的代码为如下,增加了下面代码的 4/10/16行,4行表示建立一个写入流(如果写入的文件不存在,会自动创建一个文件),10行表示往文件写入东西,16行表示关闭写入流。
var fs = require("fs"); var rs = fs.createReadStream("a.txt"); var ws = fs.createWriteStream("b.txt"); //写入流 rs.setEncoding("utf-8"); //监听当有数据流入的时候 rs.on("data",function(chunc){ console.log("..."); //读的过程中,我们打印三个点。 ws.write(chunc,"utf-8"); //向文件写入东西 }); rs.on("end",function(){ console.log("没有数据了"); ws.end(); //关闭写入流 });
这样我们异步的读取和写入文件就实现了
总结
之所以经常会混乱是因为没有说清楚讨论的是哪一部分, 所以同步异步讨论的对象时双方,而阻塞非阻塞讨论的市对象自身。