问题
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);
}
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;
}
}
}
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.
Thanks in advance!
回答1:
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;
}
}
}
...where delay
might be:
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
When you call bubbleSort
, it will return a promise almost immediately, and continue its logic asynchronously until it's done, then settle the promise.
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.
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();
});
});
...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.
来源:https://stackoverflow.com/questions/62025637/how-to-pause-javascript-for-loop-animating-bubble-sort