如何暂停javascript循环(动画气泡排序)? [英] How to pause javascript for loop (animating bubble sort)?

查看:98
本文介绍了如何暂停javascript循环(动画气泡排序)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am new to javascript so sorry if I am misunderstanding how the language does some stuff, I am building a sorting algorithms visualizer which orders blocks by their hue value (using chroma-js library) : Each item in screenObject.items is a Color object

//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,则只能在每次通过后进行颜色更新,而不是在每次比较/交换之后进行更新(在比较颜色时,我想添加特殊的样式):

The issue I have is that this colors only update after the program is completed, and if I add an setIterval, or setTimeout I have only been able to make the colors update after each pass, instead of after each comparison/swap (I want to add special styling when the colors are being compared):

  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的颜色.

I want to be able to see the colours update after every single comparison for example swap(1, 2) the user sees 1 get 2's color and 2 get 1's color.

提前谢谢!

推荐答案

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

I'm going to assume you're doing this on a browser. You need to yield back to the event loop in order for other things to happen, such as repainting the page. Probably the simplest thing is to make your bubbleSort an async method and have it await something, such as a timer callback:

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 时,它将几乎立即返回一个Promise,并异步继续其逻辑直到完成,然后兑现Promise.

When you call bubbleSort, it will return a promise almost immediately, and continue its logic asynchronously until it's done, then settle the promise.

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

Note that async functions were added in ES2018. The're well-supported by modern browsers, but you may need a tool like Babel to transpile for older ones.

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

If you want to be even more precise, you could use requestAnimationFrame rather than setTimeout with a zero-length timeout. Here's a somewhat counter-intuitive function for that:

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

...您将使用它代替 delay :

...which you'd use in place of delay:

await nextFrame();

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

(In your case, you don't need to pass any callback, the default do-nothing callback is sufficient.)

我在此推文中解释了该功能的奇怪设计的原因.转而问是否真的需要对它进行奇怪的设计.

I explain the reason for the odd design of that function in this tweet (which in turn is asking whether it really needs to be designed that oddly).

另一种方法是稍微反转您的逻辑并使用生成器函数来生成每个交换.然后,您可以循环驱动该发电机.这是我在回答有关可视化排序的另一个问题时使用的方法 .

Another approach is to invert your logic a bit and use a generator function that generates each swap. Then you can drive that generator in a loop. That's the approach I used when answering this other question about visualizing buble sort.

这篇关于如何暂停javascript循环(动画气泡排序)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆