1. 源码简介
yocto-queue在上一期 p-limit 里用被引用到,粗略理解是封装的一个队列数据结构的包,队列是种数据结构,遵循先进先出原则。
官方简介:Tiny queue data structure。
官方提示了一句,You should use this package instead of an array if you do a lot of Array#push() and Array#shift() on large arrays, since Array#shift() has linear time complexity O(n) while Queue#dequeue() has constant time complexity O(1). That makes a huge difference for large arrays.
意思是,如果你在一个大型数组上操作数据,请考虑使用这个包,而不是Array函数,因为Array操作时间复杂度为O(n),但队列的时间复杂度为O(1), That makes a huge difference for large arrays。😀
2. 测试用例
先介绍一下队列queue的几个API:
- enqueue(value) 入队
- dequeue() 出队
- clear() 清除队列
- size 队列大小
测试用例也不多,来看看。
.enqueue(),测试进队。(非常通俗易懂)
test('.enqueue()', t => {
const queue = new Queue();
queue.enqueue('🦄');
t.is(queue.dequeue(), '🦄');
queue.enqueue('🌈');
queue.enqueue('❤️');
t.is(queue.dequeue(), '🌈');
t.is(queue.dequeue(), '❤️');
});
.dequeue(),测试出队,这里还测试了队列没有值的情况。
test('.dequeue()', t => {
const queue = new Queue();
t.is(queue.dequeue(), undefined);
t.is(queue.dequeue(), undefined);
queue.enqueue('🦄');
t.is(queue.dequeue(), '🦄');
t.is(queue.dequeue(), undefined);
});
clear(),清除队列。测试了队列没有值,队列存在一个值,队列存在多个值的清除情况。
test('.clear()', t => {
const queue = new Queue();
queue.clear();
queue.enqueue(1);
queue.clear();
t.is(queue.size, 0);
queue.enqueue(1);
queue.enqueue(2);
queue.enqueue(3);
queue.clear();
t.is(queue.size, 0);
});
size,测试队列长度,测试了队列空值,清除队列后,执行入队操作,执行出队操作等边界情况。
iterable,
test('iterable', t => {
const queue = new Queue();
queue.enqueue('🦄');
queue.enqueue('🌈');
t.deepEqual([...queue], ['🦄', '🌈']);
});
3. 源码简解
```javascript // 创建Node节点 class Node { // 值 value; // next指针 next; constructor(value) {
this.value = value;
} } export default class Queue { // 头指针指向的Node
head;
// 尾指针指向的Node
tail;
// 队列长度
size;
constructor() { // 先初始化一个空的队列
this.clear();
} // 入队操作 enqueue(value) { // 把value转换成Node节点
const node = new Node(value);
// 如果头指针节点存在的话
if (this.#head) {
// 把尾指针节点的next指向当前的node, #tail.next -> node, #tail -> node
this.#tail.next = node;
this.#tail = node;
} else {
// 第一个值,#head -> node, #tail -> node
this.#head = node;
this.#tail = node;
}
// 长度+1
this.#size++;
} // 出队操作 dequeue() { // 把头出掉
const current = this.#head;
// 简单判空处理
if (!current) {
return;
}
// #head -> #head.next, 移动一下指针
this.#head = this.#head.next;
// 长度-1
this.#size--;
// 返回这个出队的值
return current.value;
} // 清除队列,也就是初始化 clear() {
this.#head = undefined;
this.#tail = undefined;
this.#size = 0;
} // 获取size get size() {
return this.#size;
} // 这里什么时候会被调用呢?是当进入for…of,就会进入Symbol.iterator这个方法。 // 如果没找到,就会报错,像数组,对象都是有内置该方法。 // 该方法必须返回一个迭代器,一个有next方法的对象。 // *号代表生成器,调用生成器函数会产生一个Generator的迭代器对象。 // 通过调用生成器函数的next(),generator函数将执行,直到遇到yield关键字。
- Symbol.iterator {
// 从头开始迭代
let current = this.#head;
// 循环取值
while (current) {
// 遇到yield,return值
// next给当前current,往下迭代yield current.value;
} } }current = current.next;
4. 总结
重新学习生成器和迭代器的概念,这个源码可以多写一写,常温习。