node课程宏任务队列举例队列:timer poll check
node事件循环:
清空微任务队列有两个时机
- 在第一次同步代码执行完毕后
- timer中所有宏任务执行完之后,会依次切换队列 timer —> poll —> check(比如:在切换为timer 切换为 poll 之前)
- 判断这2个的执行时机:前提条件,node中,setTimeout(() => {}, 0) = setTimeout(() => {}, 1) ```javascript
setTimeout(() => {}, 0) setImmediate(() => {}) // event loop 启动需要时间,如果直接运行这两行代码,会得到不确定顺序,是因为,启动如果花了1ms,会进入第二轮循环执行setTimeout,再setImmediate;如果没有1ms,那还是停留在第一轮循环,所以进入check阶段执行setiImmediate,再第二轮循环setTimeout
**浏览器事件循环:**<br />清空微任务队列有两个时机
- 在第一个同步任务执行完毕后清空微任务队列
- 在当前宏任务结束之后清空微任务队列
- <br />
相同点:在同步代码执行完毕后都会去看一眼微任务队列是否有微任务<br />不同点:(其实它们没有什么可比性啊!!!)
- node事件循环中nextTick的优先级高于promise的优先级
在基础讲解中学到的点
- 会调试了
- 写功能先写外层结构,先写需要方法名,再填充内容
- 写一步测试一步
- 学习源码的方法:先进入调试,再自己模拟实现,这样能加深印象,这也是快速了解一种新的API的方法,比拿着源码看有用多了
- 学习源码就是要先会使用,再看源码
- 边界条件应该放在独立功能的入口处
<a name="zgm1h"></a>
## linkList实现
```javascript
/*
01 node + head + null
02 head --->null
03 size
04 next element
05 增加 删除 修改 查询 清空
*/
class Node {
constructor(element, next) {
this.element = element
this.next = next
}
}
class _getNode(index) {
// second step: side condition
if (index < 0 || index >= this.size) {
throw new Error('cross the side')
}
// first step: main logic
let currentNode = this.head
for (let i = 0; i < index; i++) {
currentNode = currentNodex.next
}
}
class LinkList {
constructor(head, size) {
this.head = null
this.size = 0
}
add(index, element) {
if (arguments.length === 1) {
element = index
index = this.size
}
if (index < 0 || index > this.size) {
throw new Error('cross the border')
}
if (index === 0) {
let head = this.head
this.head = new Node(element, head)
} else {
let preNode = this._getNode(index-1)
preNode.next = new Node(element, preNode.next)
}
this.size++
}
remove(index) {
if (index === 0) {
// why can not head.next = head.next.next?
let head = this.head
this.head = head.next
} else {
let preNode = this._getNode(index-1)
preNode.next = preNode.next.next
}
}
set(index, element) {
let node = this._getNode(index)
node.element = element
}
get(index) {
return this._getNode(index)
}
clear() {
this.head = null
this.size = 0
}
}
// test
const l1 = new LinkedList()
l1.add('node1')
console.log(l1)
// result
LinkedList { head: Node { element: 'node1', next: null }, size: 1 }
require实现
const { dir } = require('console')
const fs = require('fs')
const path = require('path')
const vm = require('vm')
function Module (id) {
this.id = id
this.exports = {}
console.log(1111)
}
Module._resolveFilename = function (filename) {
// 利用 Path 将 filename 转为绝对路径
let absPath = path.resolve(__dirname, filename)
// 判断当前路径对应的内容是否存在()
if (fs.existsSync(absPath)) {
// 如果条件成立则说明 absPath 对应的内容是存在的
return absPath
} else {
// 文件定位
let suffix = Object.keys(Module._extensions)
for(var i=0; i<suffix.length; i++) {
let newPath = absPath + suffix[i]
if (fs.existsSync(newPath)) {
return newPath
}
}
}
throw new Error(`${filename} is not exists`)
}
Module._extensions = {
'.js'(module) {
// 读取
let content = fs.readFileSync(module.id, 'utf-8')
// 包装
content = Module.wrapper[0] + content + Module.wrapper[1]
// VM
let compileFn = vm.runInThisContext(content)
// 准备参数的值
let exports = module.exports
let dirname = path.dirname(module.id)
let filename = module.id
// 调用
compileFn.call(exports, exports, myRequire, module, filename, dirname)
},
'.json'(module) {
let content = JSON.parse(fs.readFileSync(module.id, 'utf-8'))
module.exports = content
}
}
Module.wrapper = [
"(function (exports, require, module, __filename, __dirname) {",
"})"
]
Module._cache = {}
Module.prototype.load = function () {
let extname = path.extname(this.id)
Module._extensions[extname](this)
}
function myRequire (filename) {
// 1 绝对路径
let mPath = Module._resolveFilename(filename)
// 2 缓存优先
let cacheModule = Module._cache[mPath]
if (cacheModule) return cacheModule.exports
// 3 创建空对象加载目标模块
let module = new Module(mPath)
// 4 缓存已加载过的模块
Module._cache[mPath] = module
// 5 执行加载(编译执行)
module.load()
// 6 返回数据
return module.exports
}
let obj = myRequire('./v')
let obj2 = myRequire('./v')
console.log(obj.age)
js中任务执行和node中任务执行对比
// js中任务执行
setTimeout(() => {
console.log('s1')
Promise.resolve().then(() => {
console.log('p2')
})
Promise.resolve().then(() => {
console.log('p3')
})
})
Promise.resolve().then(() => {
console.log('p1')
setTimeout(() => {
console.log('s2')
})
setTimeout(() => {
console.log('s3')
})
})
// p1 s1 p2 p3 s2 s3
// node中任务执行
setTimeout(() => {
console.log('s1')
Promise.resolve().then(() => {
console.log('p1')
})
process.nextTick(() => {
console.log('t1')
})
})
Promise.resolve().then(() => {
console.log('p2')
})
console.log('start')
setTimeout(() => {
console.log('s2')
Promise.resolve().then(() => {
console.log('p3')
})
process.nextTick(() => {
console.log('t2')
})
})
console.log('end')
// start, end, p2, s1, s2 , t1, t2, p1, p3
控制写入速度
/**
* 需求:“拉勾教育” 写入指定的文件
* 01 一次性写入
* 02 分批写入
* 03 中文一次就是3位,我还疑惑为啥打印了4次
* 对比:
*/
let fs = require('fs')
let ws = fs.createWriteStream('test.txt', {
highWaterMark: 3
})
// ws.write('拉勾教育')
let source = "拉勾教育".split('')
let num = 0
let flag = true
function executeWrite () {
flag = true
while(num !== 4 && flag) {
flag = ws.write(source[num])
num++
}
}
executeWrite()
ws.on('drain', () => {
console.log('drain 执行了')
executeWrite()
})
// pipe
// result
drain 执行了
drain 执行了
drain 执行了
drain 执行了
redis的由来
- 因为mysql数据库的开发者不满足于性能,所以redidis一定是一款高性能的
- remote dictionary server 远程字典服务器