如何暂停JavaScript循环(动画气泡排序)?

时间:2020-05-26 15:24:47

标签: javascript for-loop animation javascript-objects

我是javascript的新手,如果您误解了该语言是如何处理某些事情的, 我正在构建一个排序算法可视化程序,该可视化程序按色相值对块进行排序(使用chroma-js库): screenObject.items中的每个项目都是一个Color对象

//color objects are what I am sorting
class Color {
  constructor(div, color, value) {
    //this div on the html page
    this.div = div;
    this.color = color;
    //hue value of the color
    this.value = value;
  }
  update(color, value) {
    this.color = color;
    this.value = value;
    this.div.style.backgroundColor = color;
  }
}

class ScreenObject {
  constructor() {
    //this is an array of color objects
    this.items = [];
  }
  bubbleSort() {
    let solved = false;
    while (!solved) {
      let swaps = 0;
      this.items.forEach((item, index) => {
        if (index > 0) {
          swaps += compare(this.items[index - 1], item);
        }
      });
      if (swaps === 0) {
        solved = true;
      }
    }
  }
}

function compare(color1, color2) {
  if (color1.value > color2.value) {
    swap(color1, color2);
    return 1;
  } else {
    return 0;
  }
}

function swap(color1, color2) {
  colorStore = color1.color;
  valueStore = color1.value;
  color1.update(color2.color, color2.value);
  color2.update(colorStore, valueStore);
}

我遇到的问题是,仅在程序完成后才更新颜色,如果我添加setIterval或setTimeout,则只能在每次通过后进行颜色更新,而不是在每次比较/交换之后进行更新(在比较颜色时,我想添加特殊的样式):

  bubbleSort() {
    let solved = false;
    while (!solved) {
      let swaps = 0;
      setInterval(() => {
        this.items.forEach((item, index) => {
          if (index > 0) {
            swaps += compare(this.items[index - 1], item);
          }
        });
      }, 50);
      if (swaps === 0) {
        solved = true;
      }
    }
  }

我希望能够在每次比较后看到颜色更新,例如swap(1,2),用户看到1得2的颜色和2得1的颜色。

谢谢!

1 个答案:

答案 0 :(得分:0)

我将假设您正在浏览器上执行此操作。您需要让事件循环回去,以便发生其他事情,例如重新绘制页面。可能最简单的事情是使您的bubbleSort成为async方法,并使其await一些东西,例如计时器回调:

async bubbleSort() { // <=== Note the `async`
  let solved = false;
  while (!solved) {
    let swaps = 0;
    // *** No `setInterval`
    for (const [index, item] of this.items.entries()) {
      if (index > 0) {
        const result = compare(this.items[index - 1], item);
        if (result) {         // *** Did it do something?
          swaps += result;
          await delay(0);     // *** Wait for it to be redrawn
        }
      }
    });
    if (swaps === 0) {
      solved = true;
    }
  }
}

...其中delay可能是:

function delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

调用bubbleSort时,它将几乎立即返回一个承诺,并异步继续其逻辑直到完成,然后兑现该承诺。

请注意,在ES2018中添加了async函数。现代浏览器已经很好地支持了这一功能,但您可能需要像Babel这样的工具才能将其移植到较旧的浏览器中。


如果您想更加精确,可以将requestAnimationFrame而不是setTimeout设为零长度超时。这是一个有点违反直觉的功能:

const nextFrame = (cb = () => {}) => new Promise(resolve => {
    requestAnimationFrame(() => {
        cb();
        resolve();
    });
});

...您将用来代替delay

await nextFrame();

(在您的情况下,您无需传递任何回调,默认的不执行回调就足够了。)

我在this tweet中解释了对该函数进行奇数设计的原因(这反过来又询问是否真的需要对它进行奇数设计)。


另一种方法是稍微反转逻辑并使用生成器函数来生成每个交换。然后,您可以循环驱动该发电机。这就是我使用的when answering this other question about visualizing buble sort方法。