Node原理
require的原理
模拟实现require
// myRequire.js
const fs = require("fs");
const path = require("path");
const { Script } = require("vm");
const myRequire = function (filename) {
const file = fs.readFileSync(path.join(__dirname, filename), { encoding: "utf-8" });
/*
* 读取到的file文件添加到函数中
* @require file 中可以使用require
* @module file 中可以使用module.exports导出
* @exports file 中可以使用exports导出
*/
const content = `(function(require, module, exports){
${file}
})`;
const res = new Script(content).runInThisContext(); // 执行content
const module = {
exports: {},
};
res(myRequire, module, module.exports);
return module.exports;
};
module.exports = myRequire;
使用测试
// add.js
console.log("-----require", require); // 此时的require已经是我们自定义的myRequire函数
const add = function (a, b) {
return a + b;
};
module.exports = {
add,
};
// main.js
const myRequire = require("./myRequire");
const { add } = myRequire("./add.js");
add(1, 2); // 3
Buffer
const buf1 = Buffer.alloc(5); // 新建buffer期望长度
const buf2 = Buffer.from("天真"); // 传入的buffer数据拷贝到新建的Buffer实例
console.log("buf2==================", buf2);
const buf3 = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]); // 八位字节array创建新Buffer
console.log("buf3==================", buf3.toString()); // buffer
// 拼接
let newBuf = Buffer.alloc(6);
buf2.copy(newBuf, 0, 0, 6);
console.log("newBuf==================", newBuf.toString()); // 天真
readFile 读写文件
readFile(path.resolve(__dirname, "./a.js"), "utf-8", function (err, data) {
writeFile(path.resolve(__dirname, "./a_copy.js"), data, function (error) {
console.log("error==================", error);
});
});
Buffer读写文件
// Buffer
let buf = Buffer.alloc(30);
open(path.resolve(__dirname, "./a.js"), "r", function (err, rfd) {
read(rfd, buf, 0, 30, 0, function (err, bytesRead) {
console.log("buf==================", buf.toString());
open(path.resolve(__dirname, "./b.js"), "w", 0o666, function (err, wfd) {
write(wfd, buf, 0, 30, 0, function (err, written) {
close(wfd);
});
});
});
});
Stream
const fs = require("fs");
const path = require("path");
const res = fs.createReadStream(path.resolve(__dirname, "../a.js"), {
flags: "r",
start: 0,
end: 1000,
highWaterMark: 20,
autoClose: true,
emitClose: true,
});
let arr = [];
res.on("open", function (fd) {
console.log("fd", fd);
});
res.on("data", function (data) {
console.log("end", data);
arr.push(data);
});
res.on("end", function (data) {
console.log("end", Buffer.concat(arr).toString());
});
发布订阅
function EventEmitter() {
this.events = {};
}
EventEmitter.prototype.on = function (eventName, fn) {
const events = this.events[eventName] || (this.events[eventName] = []);
events.push(fn);
};
EventEmitter.prototype.emit = function (eventName, ...res) {
if (!this.events[eventName]) return;
this.events[eventName].forEach((cb) => {
cb(...res);
});
};
EventEmitter.prototype.off = function (eventName, cb) {
this.events[eventName] = this.events[eventName].filter((item) => item !== cb && item.cb !== cb);
};
EventEmitter.prototype.once = function (eventName, cb) {
const once = (...res) => {
cb(...res);
this.off(eventName, once);
};
once.cb = cb;
this.on(eventName, once);
};
const eventBus = new EventEmitter();
function handle(msg) {
console.log("handler1==================", msg);
}
function handle2(msg) {
console.log("handler2==================", msg);
}
function handle3(msg) {
console.log("handler3 once==================", msg);
}
eventBus.on("data", handle);
eventBus.on("data", handle2);
eventBus.once("data", handle3);
eventBus.off("data", handle3);
eventBus.emit("data", "hello tianzhen");
// eventBus.off('data', handle)
// eventBus.off('data', handle2)
// eventBus.emit('data', 'hello tianzhen 1')
// eventBus.emit('data', 'hello tianzhen 2')
分析:
- 同一事件可能多次发布,所以用数组存储
off
其实就是从数组里移除事件名once
执行后需要将自己移除(off)- 移除
once
事件的时候,由于once事件是在内部定义的,需要单独处理
全局对象
- __dirname
- __filename
- process.cwd()
事件循环
JS
引擎本身不实现事件循环,它是由宿主环境实现的。js -> 浏览器,node -> libuv
Node中的其他异步方法
- 文件I/O
- setImmediate(libuv实现的一个api)同步任务执行完成后立马执行
- server.close, socket.on(); 关闭回调
process.nextTick是一个微任务,先于promise执行
- poll阶段:轮训等待新的连接和请求等事件,执行IO回调等
简单
setTimeout(() => {
console.log("timeout");
}, 0);
Promise.resolve().then(() => {
console.error("promise");
});
process.nextTick(() => {
console.error("nextTick");
});
// nextTick promise timeout
绕脑
const fs = require("fs");
fs.readFile(__filename, (data) => {
console.log("readFile");
setTimeout(() => {
console.log("timeout");
}, 0);
setImmediate(() => {
console.log("setImmediate");
});
});
// 输出:readFile、setImmediate、timeout
- 第一次循环没有需要执行的异步任务队列
- 第二次只有poll阶段有IO回调任务,输出 ‘readFile’
- poll阶段检测如果有
setImmediate
任务队列,则进入check
- 执行close callbacks
- 第三次进入
timer
阶段,输出timeout
思考题
async function async1() {
console.log("async1 started");
await async2();
console.log("async end");
}
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");
});
new Promise((resolve) => {
console.log("promise1");
resolve();
console.log("promise2");
}).then(() => {
console.log("promise.then");
});
console.log("script end.");