node事件循环 EventEmitter 异步I/O Buffer缓冲区 模块
node.js事件循环
node.js单进程,单线程的程序
每一个api都支持回调
所有的事件机制都是设计模式中的
一共是23种设计模式
http://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/observer.html
一个对象发生改变的时候,将自动通知其他对象,其他对象将相应的做出反应。发生改变的对象为观察目标,被通知的对象为观察者。一个观察目标可以对应多个观察者,而这些观察者之间没有任何联系,可以根据需要增加观察者,使得系统更加容易扩展,依赖关系为一对多,又被称为模型-视图模式
事件驱动程序
web server接收到请求,将其关闭,进行处理,然后接着服务下一个web请求。
当请求完成以后,放回处理队列,当到达队列开头的时候,将其结果返回给用户
即非阻塞式I/O 事件驱动I/O
会有一个主循环来监听事件,当检测到事件以后,触发回调函数
代码
PS C:\Users\mingm\Desktop\test> node main.js 连接成功 数据接收成功 程序执行完毕 PS C:\Users\mingm\Desktop\test>
// 引入一个 events 模块 var events = require('events'); // 创建 eventEmitter对象 var eventEmitter = new events.EventEmitter(); // 创建connection事件的处理程序 var connectHandLer = function connected() { console.log('连接成功'); // 触发 data_received 事件 eventEmitter.emit('data_received'); }; // 绑定cinnection事件处理程序 eventEmitter.on('connection', connectHandLer); // 绑定data_received事件,并完成处理程序的书写 eventEmitter.on( 'data_received', function() { console.log('数据接收成功'); } ); // 触发 connection 事件 eventEmitter.emit('connection'); console.log('程序执行完毕');
程序的执行过程,先完成各种绑定,触发connection事件以后,寻找绑定的处理程序,为connected(),然后,执行一半,又被触发,data_received事件。寻找绑定的处理程序。一个匿名函数,执行,事件全部完成,执行最后一句,程序执行完毕。
多用户执行的情况下,触发事件以后,若处理程序正被其他用户占用,排队,直到前方全部处理完成以后,接着该用户使用处理程序进行处理。
EventEmitter
node所有的异步I/O操作在完成的时候都会发送到一个事件到达事件队列。node里的对象能够分发事件
产生的事件的对象都是events.EventEmitter的实例
EventEmitter类
events模块提供一个对象,它是对事件的触发和事件的监听的封装
PS C:\Users\mingm\Desktop\test> node main.js 事件触发 PS C:\Users\mingm\Desktop\test>
过五秒后响应
// event.js文件 var EventEmitter = require('events').EventEmitter; var event = new EventEmitter(); // 创建一个event对象 event.on('some_event', function(){console.log('事件触发');}); setTimeout(function(){event.emit('some_event');}, 5000);
大概解释一下这段代码
前两句很简单,后两句说一下,event的对象注册了一个事件的监听器。这个事件的监听器为一个匿名函数,事件名称为some_event,当5000秒以后被触发先对象event发送一个事件some_event触发了匿名函数即监听器,监听器被执行。
其中EventEmitter的每个事件由一个事件名和若干参数组成,对于一个事件能有若干的监听器,当事件触发的时候,监听器会被依次调用,事件参数作为回调函数的参数进行传递,需要注意的是,监听器会被依次调用
error事件
error是一个单独列出来的事件,一般要为其绑定一个监听器,因为node如果抛出error,若没有监听器执行,将会直接退出执行,并返回错误
Buffer缓冲区
处理TCP或者文件流的时候,需要用到二进制的数据,定义了一个Buffer类,该类用于专门存放二进制数据的缓冲区
Buffer与字符编码
const buf = Buffer.from('ming', 'ascii'); // 声明一个只读的变量 console.log(buf.toString('hex')); console.log(buf.toString('utf-8'));
PS C:\Users\mingm\Desktop\test> node main.js 6d696e67 ming PS C:\Users\mingm\Desktop\test>
创建一个buffer类,并完成读取写入
PS C:\Users\mingm\Desktop\test> node main.js 23456789:;<=>?@ABCDEFGHIJK 23456789:;<=>?@ABCDEFGHIJK PS C:\Users\mingm\Desktop\test>
buf = Buffer.alloc(26); for(var i = 0; i < 26; i++){ buf[i] = 50 + i; }; console.log(buf.toString('ascii')); console.log(buf.toString('utf8'));
将buffer转换为jsoon
PS C:\Users\mingm\Desktop\test> node main.js {"type":"Buffer","data":[1,2,3,4,5]} PS C:\Users\mingm\Desktop\test>
const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); const json = JSON.stringify(buf); console.log(json);
将JSON转换为Buffer
一个转换方法JSON.parse
> JSON.parse('{"1": 3, "2": [4, 5, 6]}', function (k, v) { ... console.log(k); // 输出当前的属性名,从而得知遍历顺序是从内向外的, ... console.log("----------"); // 最后一个属性名会是个空字符串。 ... console.log(v); ... }); 1 ---------- 3 0 ---------- 4 1 ---------- 5 2 ---------- 6 2 ---------- [ <3 empty items> ] ---------- {} undefined >
开始转换
const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); const json = JSON.stringify(buf); console.log(json); const copy = JSON.parse( // 调用JSON.parse函数,将字符串转换为对象,后一个参数为转换的函数,转换的时候将会调用 json, (key, value) => { // 此为es6的语法,一个匿名函数 console.log(value, '----', !!value, '----', value.type, '-----', value.data); return value && value.type === 'Buffer' ? Buffer.from(value.data): value; // 只留下最后一个进行转换,前面的转换全部被覆盖 } ) // 输出: <Buffer 01 02 03 04 05> console.log(copy);
PS C:\Users\mingm\Desktop\test> node main.js {"type":"Buffer","data":[1,2,3,4,5]} Buffer ---- true ---- undefined ----- undefined 1 '----' true '----' undefined '-----' undefined 2 '----' true '----' undefined '-----' undefined 3 '----' true '----' undefined '-----' undefined 4 '----' true '----' undefined '-----' undefined 5 '----' true '----' undefined '-----' undefined [ 1, 2, 3, 4, 5 ] '----' true '----' undefined '-----' undefined { type: 'Buffer', data: [ 1, 2, 3, 4, 5 ] } '----' true '----' 'Buffer' '-----' [ 1, 2, 3, 4, 5 ] <Buffer 01 02 03 04 05> PS C:\Users\mingm\Desktop\test>
Buffer的合并
var buffer1 = Buffer.from('222'); var buffer2 = Buffer.from('3333'); var buffer3 = Buffer.concat([buffer1, buffer2]); console.log(buffer3.toString());
Stream流
流为一个抽象的接口,
从流中读取数据
PS C:\Users\mingm\Desktop\test> node main.js end! 33333333333333333333333333 PS C:\Users\mingm\Desktop\test>
var fs = require('fs'); var data = ''; // 创建可读流 var readerStream = fs.createReadStream('input.txt'); // 设置编码为 utf8 readerStream.setEncoding('UTF8'); // 处理流事件 data事件 readerStream.on('data', (chunk) => {data += chunk;}); // 遇到数据读取,将读取到的内容赋值给data // 处理流事件 end事件 readerStream.on('end', () => {console.log(data);}); // 将读取到的保存到内存中的数据打印出来 // 处理事件 error readerStream.on('error', (err) => {console.log(err.stack);}); // 处理error事件,将错误输出,避免程序的运行中断 console.log('end!');
写入流
PS C:\Users\mingm\Desktop\test> node main.js 程序执行完毕 写入完成 PS C:\Users\mingm\Desktop\test>
var fs = require('fs'); var data = '这是一段示例'; // 创建一个可以写入的流,写入到文件output.txt中 var writerStream = fs.createWriteStream('output.txt'); // 使用 utf8 编码写入数据 writerStream.write(data, 'UTF8'); // 标记文件末尾 writerStream.end(); // 处理流事件 --> data, end, add error writerStream.on('finish', () => {console.log('写入完成');}); writerStream.on('error', () => {console.log(err.stack);}); console.log('程序执行完毕');
管道流
把两个文件之间建立流,让一个文件的数据流向另外一个文件
PS C:\Users\mingm\Desktop\test> node main.js 程序执行完毕 PS C:\Users\mingm\Desktop\test>
var fs = require('fs'); // 创建一个可读流 var readerStream = fs.createReadStream('input.txt'); // 创建一个可写流 var writerStream = fs.createWriteStream('output.txt'); // 管道读写操作 // 将两个流通过管道连接起来 readerStream.pipe(writerStream); console.log('程序执行完毕');
这里需要注意的是,由于流的影响,导致在操作的时候,会覆盖掉要写入文件的内容,原先的内容会被覆盖
链式流
PS C:\Users\mingm\Desktop\test> node main.js 文件压缩完成 PS C:\Users\mingm\Desktop\test>
var fs = require('fs'); var zlib = require('zlib'); // 压缩 input.txt文件为 input.txt.gz fs.createReadStream('input.txt') // 创建一个可读流 .pipe(zlib.createGzip()) // 将创建的可写流和压缩流连接 .pipe(fs.createWriteStream('input.txt.gz')); // 在创建可写流,将三个流连接到一起 console.log('文件压缩完成');
内容未被覆盖的写入
var fs = require('fs'); var read = fs.createReadStream('output.txt'); var write = fs.createWriteStream('input.txt'); read.pipe(write); console.log('执行完毕');
具体详细,请查文档
https://nodejs.org/api/fs.html#fs_fs_createwritestream_path_options
https://nodejs.org/api/fs.html#fs_file_system_flags
文档写的很详细,后面的参数为一个对象,通过对象即可进行修改
模块
模块之前已经阐述过一部分,这里阐述服务器端的模块
服务器端的模块
node中自带了一个http模块,在代码中请求他,并将返回值赋值给本地变量。即本地变量变成了一个拥有所有http模块所提供的公共方法的对象。
node中有四个模块,(原生模块和三种文件模块)
从文件模块的缓存中添加
原生模块和文件模块优先级不同,都会优先从文件模块的缓存中加载以及存在的模块
从原生模块加载
原生模块的优先级低于文件模块缓存的优先级。原生模块也有缓存区
从文件加载
这个上一篇以及阐述完成,不在阐述
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
C# 通过进程名/进程Id 操作窗口/程序
1. 判断窗口是否存在 1 private bool IsWindowExist(IntPtr handle) 2 { 3 return (!(GetWindow(new HandleRef(this, handle), 4) != IntPtr.Zero) && IsWindowVisible(new HandleRef(this, handle))); 4 } 5 6 [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)] 7 public static extern IntPtr GetWindow(HandleRef hWnd, int uCmd); 8 9 [DllImport("user32.dll", CharSet = CharSet.Auto)] 10 public static extern bool IsWindowVisible(HandleRef hWnd); 2. 获取窗口句柄 1 /// <summary> 2 /// 获取应用程序窗口句柄 ...
- 下一篇
C# 跨进程 设置窗口owner
窗口间跨进程通信 1. 发送方 1 public const int WM_InsertChart_Completed = 0x00AA; 2 3 //查找窗口 4 [DllImport("User32.dll", EntryPoint = "FindWindow")] 5 public static extern IntPtr FindWindow(string lpClassName, string lpWindowName); 6 7 //发送信息 8 [DllImport("User32.dll", EntryPoint = "SendMessage")] 9 public static extern int SendMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam); 10 11 private void PostMessage() 12 { 13 var procs = Process.GetProcessesByName("MyExample"); 14 15 foreach (var process i...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7安装Docker,走上虚拟化容器引擎之路
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Docker安装Oracle12C,快速搭建Oracle学习环境
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果