Buffer
准确的来说,Buffer 是一种计算机中 数据流 结构,计算机中是以二进制的方式,进行数据存取的,而 js 在一开始,没有文件读写能力的,就要借助 Buffer 来实现一些缓冲区的内容。
Buffer 一般用于表示固定长度的缓冲区序列。
Buffer 的声明
let buf1 = Buffer.alloc(5); // 分配 size 个字节的新 Buffer,单位是字节
console.log(buf1); // <Buffer 00 00 00 00 00>
let buf2 = Buffer.from('麓一'); // node 中一般编码使用的是 utf-8, 所以一个汉字,是 3 个字节
console.log(buf2); // <Buffer e9 ba 93 e4 b8 80>
let buf3 = Buffer.from([0xe9, 0xba, 0x93]);
console.log(buf3); // <Buffer e9 ba 93>
console.log(buf3.toString()); // 麓
let buf4 = Buffer.from('a'); // node 一个字母,是 2 个字节
console.log(buf4); // <Buffer 61>
Buffer 和 String 的转换
// String -> Buffer
const buf = Buffer.from('麓一');
// Buffer -> String
const str = buf.toString();
console.log(buf); // <Buffer e9 ba 93 e4 b8 80>
console.log(str); // 麓一
Buffer 的拼接
copy
let buf1 = Buffer.from('麓一');
let buf2 = Buffer.from('麓一');
let bigBuffer = Buffer.alloc(6); // 分配 6 个字节
// 第一个 0, 表示从 0 这个位置开始拷贝,第二和第三个数字,表示拷贝从几到几的长度
// buf1.copy(bigBuffer, 0, 0, 3);
// console.log(bigBuffer.toString()); // 麓
buf1.copy(bigBuffer, 0, 0, 2);
buf2.copy(bigBuffer, 2, 2, 6);
console.log(bigBuffer.toString()); // 麓一
concat
console.log(Buffer.from('麓一')); // <Buffer e9 ba 93 e4 b8 80>
let buf1 = Buffer.from([0xe9, 0xba]);
console.log(buf1); // <Buffer e9 ba>
let buf2 = Buffer.from([0x93, 0xe4, 0xb8, 0x80]);
console.log(buf2); // <Buffer 93 e4 b8 80>
let bigBuffer = Buffer.concat([buf1, buf2], 6);
console.log(bigBuffer.toString()); // 麓一
Buffer 的截取
let buf1 = Buffer.from('麓一老师');
let buf2 = buf1.slice(0, 6);
console.log(buf1, buf1.toString()); // <Buffer e9 ba 93 e4 b8 80 e8 80 81 e5 b8 88> 麓一老师
console.log(buf2, buf2.toString()); // <Buffer e9 ba 93 e4 b8 80> 麓一
Buffer 的类型判断
let buf1 = Buffer.from('麓一老师');
const isBuffer = Buffer.isBuffer(buf1);
console.log(isBuffer); // true
Buffer 进行文件读写
const fs = require('fs');
const path = require('path');
fs.readFile(path.resolve(__dirname, './package.json'), 'utf-8', function (err, data) {
if (err) {
console.log('err >>>> ', err);
return;
}
fs.writeFile(path.resolve(__dirname, './package-copy.json'), data, function (err) {
if (err) {
console.log('err >>>> ', err);
return;
}
console.log('写入成功');
});
});
Stream 流
- 流的特点:防止淹没可用内存;
Buffer不适合大文件的读取,如果是比较小的文件可以,但是对于大文件,我们就需要使用流;
const buf = Buffer.alloc(3);
// r - read 读
// 777 - 权限
fs.open(path.resolve(__dirname, 'package.json'), 'r', 777, function (err, fd) {
// fd 文件描述符
fs.open(path.resolve(__dirname, 'copy.json'), 'w', function (err, wfd) {
// console.log(fd); // 是一个数字类型,用完需要关闭掉
function close() {
fs.close(fd, () => {});
fs.close(wfd, () => {});
}
function next() {
// buf 写入到哪个 buffer 中,从 buffer 的哪个位置开始写入 3 个字节,从文件的第 0 个位置开始读取
fs.read(fd, buf, 0, 3, 0, function (err, bytesRead) {
// bytesRead 实际读取到的个数,这个个数 不一定是 3 ,可能会小于 3
console.log(bytesRead);
if (bytesRead == 0) {
return close();
}
// 写入到文件中,从 buffer 的第 0 个位置写入 3 个字节,写入到文件的第 0 个位置
fs.write(wfd, buf, 0, 3, 0, function (err, bytesWritten) {
next();
});
});
}
next();
});
});
fs 创建可读流
const fs = require('fs');
const path = require('path');
const res = fs.createReadStream(path.resolve(__dirname, './package.json'), {
flags: 'r', // read
// start: 0, // 开始位置
// end: 20, // 结束位置
highWaterMark: 5, // 默认是 64K
autoClose: true,
emitClose: true,
});
let arr = [];
res.on('open', function (fd) {
console.log('fd >>>> ', fd);
});
res.on('data', function (data) {
console.log('data >>>> ', data);
arr.push(data);
});
res.on('end', function () {
console.log('end >>>> ', Buffer.concat(arr).toString());
});
res.on('close', function () {
console.log('close');
});
res.on('error', function () {
console.log('error');
});
/*
fd >>>> 4
data >>>> <Buffer 7b 0d 0a 20 20>
data >>>> <Buffer 22 6e 61 6d 65>
data >>>> <Buffer 22 3a 20 22 6e>
data >>>> <Buffer 6f 64 65 5f 74>
data >>>> <Buffer 68 65 6f 72 79>
data >>>> <Buffer 22 2c 0d 0a 20>
data >>>> <Buffer 20 22 76 65 72>
data >>>> <Buffer 73 69 6f 6e 22>
data >>>> <Buffer 3a 20 22 31 2e>
data >>>> <Buffer 30 2e 30 22 2c>
data >>>> <Buffer 0d 0a 20 20 22>
data >>>> <Buffer 6d 61 69 6e 22>
data >>>> <Buffer 3a 20 22 69 6e>
data >>>> <Buffer 64 65 78 2e 6a>
data >>>> <Buffer 73 22 2c 0d 0a>
data >>>> <Buffer 20 20 22 6c 69>
data >>>> <Buffer 63 65 6e 73 65>
data >>>> <Buffer 22 3a 20 22 4d>
data >>>> <Buffer 49 54 22 0d 0a>
data >>>> <Buffer 7d 0d 0a>
end >>>> {
"name": "node_theory",
"version": "1.0.0",
"main": "index.js",
"license": "MIT"
}
close
*/
文件压缩
const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
fs.createReadStream(path.resolve(__dirname, './package.json'))
.pipe(zlib.createGzip())
.pipe(fs.createWriteStream(path.resolve(__dirname, './package.json.gz')));
Event
// 手写一个发布订阅
function EventEmitter() {
this._events = {};
}
EventEmitter.prototype.on = function (eventName, callback) {
if (!this._events) this._events = {};
let eventCbList = this._events[eventName] || (this._events[eventName] = []);
eventCbList.push(callback);
};
EventEmitter.prototype.emit = function (eventName, ...rest) {
this._events[eventName] && this._events[eventName].forEach(cb => cb(...rest));
};
EventEmitter.prototype.off = function (eventName, callback) {
if (!this._events) this._events = {};
if (this._events[eventName]) {
this._events[eventName] = this._events[eventName].filter(
item => item != callback && item.cb !== callback
);
}
};
// 只执行一次
EventEmitter.prototype.once = function (eventName, callback) {
const once = (...rest) => {
callback(...rest);
this.off(eventName, once);
};
once.cb = callback;
this.on(eventName, once);
};
const emitter = new EventEmitter();
// ============ 第一种 =============
// emitter.on('data', function (msg) {
// console.log('hello' + msg);
// });
// emitter.on('data', function (msg) {
// console.log('hello2' + msg);
// });
// setTimeout(() => {
// emitter.emit('data', ' msg');
// }, 500);
/*
hello msg
hello2 msg
*/
// ============ 第二种 =============
// emitter.on('data', function (msg) {
// console.log('hello' + msg);
// });
// const handle = function (msg) {
// console.log('hello2' + msg);
// };
// emitter.on('data', handle);
// setTimeout(() => {
// emitter.emit('data', ' msg');
// emitter.off('data', handle);
// emitter.emit('data', ' msg');
// }, 500);
/*
hello msg
hello2 msg
hello msg
*/
// ============ 第三种 =============
emitter.on('data', function (msg) {
console.log('hello' + msg);
});
const handle = function (msg) {
console.log('hello2' + msg);
};
emitter.once('data', handle);
setTimeout(() => {
emitter.emit('data', ' msg');
emitter.emit('data', ' msg');
}, 500);
/*
hello msg
hello2 msg
hello msg
*/
cluster 集群
const http = require('http');
// 集群
const cluster = require('cluster');
// 操作系统
const os = require('os'); // operating system 操作系统
const cpu_num = os.cpus(); // cpu 核数
// console.log('cpu_num ======== ', cpu_num.length);
// 判断进程是否为主进程
if (cluster.isMaster) {
for (let i = 0; i < cpu_num.length; i++) {
// 创建一个子进程(衍生新的工作进程,只能从主进程调用)
cluster.fork();
}
} else {
http
.createServer((req, res) => {
res.end(`childPid ${process.pid}`);
})
.listen(3000);
console.log(`Worker ${process.pid} started`);
}
child_process 开启子进程
./index.js
const path = require('path');
const http = require('http');
// 子进程,开启子进程
const child_process = require('child_process');
// 操作系统
const os = require('os'); // operating system
const cpu_num = os.cpus();
const server = http
.createServer((req, res) => {
res.setHeader('Content-type', 'application/json');
res.end(JSON.stringify({ pid: process.pid }));
})
.listen(3000);
for (let i = 0; i < cpu_num.length - 1; i++) {
let cp = child_process.fork('server.js', {
cwd: path.resolve(__dirname),
});
cp.send('serverMsg', server, function () {
console.log('===== send =======');
});
}
./server.js
const http = require('http');
process.on('message', function (message, server) {
console.log(message, process.pid, 'child');
http
.createServer((req, res) => {
res.setHeader('Content-type', 'application/json');
res.end(JSON.stringify({ pid: process.pid }));
})
.listen(server);
});
事件循环
浏览器中
一般情况下:
-
宏任务
Input events、keyPress、timers
-
微任务
promise、await async、generator
-
frame -
resize、scroll、动画、媒体查询器 -
RAF -
requestAnimationFrame callbacks、IntersectionObserver -
Layout -
ResizeObserver -
Paint -
合成
-
RequestIdleCallback
Node 的事件循环
同步的代码
|
process.nextTick / promise...
|
┌───────────────────────────┐
┌─>│ timers │ 定时器: setTimeout / setInterval
│ └─────────────┬─────────────┘
| process.nextTick / promise...
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │ 执行延迟到下一个循环迭代的 I/O 回调
│ └─────────────┬─────────────┘
| process.nextTick / promise...
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │ 系统内部使用
│ └─────────────┬─────────────┘ ┌───────────────┐
| process.nextTick / promise... │ │
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
| process.nextTick / promise... │ │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │ setImmediate
│ └─────────────┬─────────────┘
| process.nextTick / promise...
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │ 关闭回调函数
└───────────────────────────┘
- 定时器:本阶段执行已经被
setTimeout()和setInterval()的调度回调函数; - 待定回调:执行延迟到下一个循环迭代的
I/O回调; idle/prepare:仅系统内部使用;- 轮询:检索新的
I/O事件,执行与I/O相关的回调(几乎所有情况下,除了关闭的回调函数,那些由计时器和setImmediate()调度的之外),其余情况node将在适当的时候在此阻塞; - 检测:
setImmediate()回调函数在这里执行; - 关闭的回调函数:一些关闭的回调函数,如:
socket.on('close', ...);
题目
async function async1() {
console.log('async1 started');
await async2();
console.log('async end'); // m1
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(() => {
console.log('setTimeout0');
setTimeout(() => {
console.log('setTimeout1');
}, 0);
setImmediate(() => {
console.log('setImmediate');
});
}, 0);
async1();
process.nextTick(() => {
console.log('nextTick'); // m0
});
new Promise(resolve => {
console.log('promise1');
resolve();
console.log('promise2');
}).then(() => {
console.log('promise.then'); // m2
});
console.log('script end');
/*
script start
async1 started
async2
promise1
promise2
script end
nextTick
async end
promise.then
setTimeout0
setImmediate
setTimeout1
*/