问题
k = []
len = 100;
time = true
cont = document.getElementsByClassName("cont")[0];
cont.innerHTML = "";
for (let i = 0; i < len; i++) {
t = Math.round(Math.random() * 800 ) + 5
k.push(t);
cont.innerHTML += "<div class='block' style = 'height:" + t + "px'></div>"
}
function reset(){
k = []
cont.innerHTML = "";
for (let i = 0; i < len; i++) {
t = Math.round(Math.random() * 800 ) + 5
k.push(t);
cont.innerHTML += "<div class='block' style = 'height:" + t + "px'> </div>"
}
}
function bubble(){
function iloop(i){
if(i < len){
setTimeout(function(){
function jloop(j){
if(j < len){
setTimeout(function(){
if (k[j] > k[j + 1]) {
let tmp = k[j];
k[j] = k[j + 1];
k[j + 1] = tmp;
}
cont.innerHTML = "";
for (let p = 0; p < len; p++) {
cont.innerHTML += "<div class='block' style = 'height:" + k[p] + "px'></div>"
}
j++;
jloop(j);
}, 100);
}
}
jloop(0);
i++;
iloop(i);
}, 100);
}
}
iloop(0);
}
.cont {
width: 100%;
height: 900px;
display: block;
background-color: pink;
padding: 0px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center; }
.cont .block {
display: inline-block;
width: 10px;
margin: auto 1px;
background-color: red;
font-size: 5px;
bottom: 0px; }
<button class="reset" onclick="reset()">Reset Array
</button>
<button class="bubble" onclick="bubble()">Bubble Sort
</button>
<div class="cont">
</div>
I am using this simple code to make a javascript visualizer for sorting algorithm but the problem is that it is very choppy and skips multiples frames while running even at a delay of 100ms. I have an i7 7700hq and gtx 1060 so I know that the problem is mostly not my laptop but my approach to it so what approach should I take
Here is a code pen version if your snippets are not working https://codepen.io/varunagarwal/pen/gOaQqbG
Edit: someone told me to make it a runnable snippet so there you go
回答1:
You have overlapping setTimeout
timers, and a lot of them being scheduled. You only want to yield back to the browser when there's a change to show, and you only want to show a given change once.
Since you're using ES2015+, I'd probably use a generator function to do the sort, yielding when something changes:
function *sortGen() {
for (let i = 0; i < len; ++i) {
for (let j = 0; j < len; ++j) {
if (k[j] > k[j + 1]) {
let tmp = k[j];
k[j] = k[j + 1];
k[j + 1] = tmp;
yield j; // *** Yield to caller, saying what changed
}
}
}
}
Then the code calling it would call its next
method, do the update, and then schedule callback for just before the next animation frame via requestAnimationFrame
(unless you want to artificially slow it down, in which case setTimeout
is fine). Animation frames happen 60 times/second (roughly every 16.666667ms) provided the browser isn't busy doing something else. Here's bubble
using the generator from the sortGen
function above:
function bubble() {
const gen = sortGen();
tick();
function tick() {
const result = gen.next();
if (!result.done) {
// *** No need to recreate all the elements, just reorder the ones that got swapped
const el = cont.children[result.value];
const next = el.nextElementSibling;
el.parentElement.insertBefore(next, el);
requestAnimationFrame(tick);
}
}
}
(You could make it an async generator and use a for-await-of
loop, but I don't think it really buys you much.)
Here's a live example; I've also included some comments in the code making other suggestions:
"use strict"; // *** Use strict mode
// *** Declare your variables (the old code relied on The Horror of Implicit Globals, which
// strict mode fixes)
let k = []; // *** Consistently use semicolons (or consistently rely on ASI)
let len = 100;
let time = true;
const cont = document.getElementsByClassName("cont")[0];
// *** Don't duplicate code, just use `reset`
reset();
function reset(){
k = [];
// *** Never use += on `innerHTML`
let html = "";
for (let i = 0; i < len; i++) {
// *** Declare your variables
const t = Math.round(Math.random() * 800 ) + 5;
k.push(t);
html += makeBlock(t);
}
cont.innerHTML = html;
}
function makeBlock(value) {
return "<div class='block' style = 'height:" + value + "px'></div>";
}
function *sortGen() {
for (let i = 0; i < len; ++i) {
for (let j = 0; j < len; ++j) {
if (k[j] > k[j + 1]) {
let tmp = k[j];
k[j] = k[j + 1];
k[j + 1] = tmp;
yield j; // *** Yield to caller, saying what changed
}
}
}
}
function bubble() {
const gen = sortGen();
tick();
function tick() {
const result = gen.next();
if (!result.done) {
// *** No need to recreate all the elements, just reorder the ones that got swapped
const el = cont.children[result.value];
const next = el.nextElementSibling;
el.parentElement.insertBefore(next, el);
requestAnimationFrame(tick);
}
}
}
.cont {
width: 100%;
height: 900px;
display: block;
background-color: pink;
padding: 0px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center;
} /* *** Don't hide closing } at the end of a line */
.cont .block {
display: inline-block;
width: 10px;
margin: auto 1px;
background-color: red;
font-size: 5px;
bottom: 0px;
} /* *** Don't hide closing } at the end of a line */
<button class="reset" onclick="reset()">Reset Array
</button>
<button class="bubble" onclick="bubble()">Bubble Sort
</button>
<div class="cont">
</div>
Also on CodePen.
For what it's worth, the async generator approach looks something like this:
const nextFrame = cb => new Promise(resolve => {
requestAnimationFrame(() => {
cb();
resolve();
});
});
function bubble() {
(async () => {
for await (const value of sortGen()) {
await nextFrame(() => {
const el = cont.children[value];
const next = el.nextElementSibling;
el.parentElement.insertBefore(next, el);
});
}
})()
.catch(error => {
// Handle/report error here...
console.error(error);
});
}
"use strict"; // *** Use strict mode
// *** Declare your variables (the old code relied on The Horror of Implicit Globals, which
// strict mode fixes)
let k = []; // *** Consistently use semicolons (or consistently rely on ASI)
let len = 100;
let time = true;
const cont = document.getElementsByClassName("cont")[0];
// *** Don't duplicate code, just use `reset`
reset();
function reset(){
k = [];
// *** Never use += on `innerHTML`
let html = "";
for (let i = 0; i < len; i++) {
// *** Declare your variables
const t = Math.round(Math.random() * 800 ) + 5;
k.push(t);
html += makeBlock(t);
}
cont.innerHTML = html;
}
function makeBlock(value) {
return "<div class='block' style = 'height:" + value + "px'></div>";
}
function *sortGen() {
for (let i = 0; i < len; ++i) {
for (let j = 0; j < len; ++j) {
if (k[j] > k[j + 1]) {
let tmp = k[j];
k[j] = k[j + 1];
k[j + 1] = tmp;
yield j; // *** Yield to caller, saying what changed
}
}
}
}
const nextFrame = cb => new Promise(resolve => {
requestAnimationFrame(() => {
cb();
resolve();
});
});
function bubble() {
(async () => {
for await (const value of sortGen()) {
await nextFrame(() => {
const el = cont.children[value];
const next = el.nextElementSibling;
el.parentElement.insertBefore(next, el);
});
}
})()
.catch(error => {
// Handle/report error here...
console.error(error);
});
}
.cont {
width: 100%;
height: 900px;
display: block;
background-color: pink;
padding: 0px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-ms-flex-line-pack: center;
align-content: center;
} /* *** Don't hide closing } at the end of a line */
.cont .block {
display: inline-block;
width: 10px;
margin: auto 1px;
background-color: red;
font-size: 5px;
bottom: 0px;
} /* *** Don't hide closing } at the end of a line */
<button class="reset" onclick="reset()">Reset Array
</button>
<button class="bubble" onclick="bubble()">Bubble Sort
</button>
<div class="cont">
</div>
Also on CodePen.
来源:https://stackoverflow.com/questions/61884893/java-script-sorting-algorithm-visualizer