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(),测试进队。(非常通俗易懂)

    1. test('.enqueue()', t => {
    2. const queue = new Queue();
    3. queue.enqueue('🦄');
    4. t.is(queue.dequeue(), '🦄');
    5. queue.enqueue('🌈');
    6. queue.enqueue('❤️');
    7. t.is(queue.dequeue(), '🌈');
    8. t.is(queue.dequeue(), '❤️');
    9. });
  • .dequeue(),测试出队,这里还测试了队列没有值的情况。

    1. test('.dequeue()', t => {
    2. const queue = new Queue();
    3. t.is(queue.dequeue(), undefined);
    4. t.is(queue.dequeue(), undefined);
    5. queue.enqueue('🦄');
    6. t.is(queue.dequeue(), '🦄');
    7. t.is(queue.dequeue(), undefined);
    8. });
  • clear(),清除队列。测试了队列没有值,队列存在一个值,队列存在多个值的清除情况。

    1. test('.clear()', t => {
    2. const queue = new Queue();
    3. queue.clear();
    4. queue.enqueue(1);
    5. queue.clear();
    6. t.is(queue.size, 0);
    7. queue.enqueue(1);
    8. queue.enqueue(2);
    9. queue.enqueue(3);
    10. queue.clear();
    11. t.is(queue.size, 0);
    12. });
  • size,测试队列长度,测试了队列空值,清除队列后,执行入队操作,执行出队操作等边界情况。

  • iterable,

    1. test('iterable', t => {
    2. const queue = new Queue();
    3. queue.enqueue('🦄');
    4. queue.enqueue('🌈');
    5. t.deepEqual([...queue], ['🦄', '🌈']);
    6. });

    3. 源码简解

    ```javascript // 创建Node节点 class Node { // 值 value; // next指针 next; constructor(value) {

    1. this.value = value;

    } } export default class Queue { // 头指针指向的Node

    head;

    // 尾指针指向的Node

    tail;

    // 队列长度

    size;

    constructor() { // 先初始化一个空的队列

    1. this.clear();

    } // 入队操作 enqueue(value) { // 把value转换成Node节点

    1. const node = new Node(value);

    // 如果头指针节点存在的话

    1. if (this.#head) {
    2. // 把尾指针节点的next指向当前的node, #tail.next -> node, #tail -> node
    3. this.#tail.next = node;
    4. this.#tail = node;
    5. } else {
    6. // 第一个值,#head -> node, #tail -> node
    7. this.#head = node;
    8. this.#tail = node;
    9. }

    // 长度+1

    1. this.#size++;

    } // 出队操作 dequeue() { // 把头出掉

    1. const current = this.#head;

    // 简单判空处理

    1. if (!current) {
    2. return;
    3. }

    // #head -> #head.next, 移动一下指针

    1. this.#head = this.#head.next;

    // 长度-1

    1. this.#size--;

    // 返回这个出队的值

    1. return current.value;

    } // 清除队列,也就是初始化 clear() {

    1. this.#head = undefined;
    2. this.#tail = undefined;
    3. this.#size = 0;

    } // 获取size get size() {

    1. return this.#size;

    } // 这里什么时候会被调用呢?是当进入for…of,就会进入Symbol.iterator这个方法。 // 如果没找到,就会报错,像数组,对象都是有内置该方法。 // 该方法必须返回一个迭代器,一个有next方法的对象。 // *号代表生成器,调用生成器函数会产生一个Generator的迭代器对象。 // 通过调用生成器函数的next(),generator函数将执行,直到遇到yield关键字。

    • Symbol.iterator { // 从头开始迭代 let current = this.#head; // 循环取值 while (current) { // 遇到yield,return值
      1. yield current.value;
      // next给当前current,往下迭代
      1. current = current.next;
      } } }

```

4. 总结

重新学习生成器和迭代器的概念,这个源码可以多写一写,常温习。