I am making a sorting algorithm visualizer using JavaScript. I have most of it down, but currently, the bars do not show up until the sorting is done, and at this point, it does show up sorted.
var list;
var ind;
var size;
var sort;
var delay;
var count;
const content = document.getElementById("content");
var height = Math.floor(window.innerHeight * 0.9);
var width = Math.floor(window.innerWidth);
function swap(arr, a, b) {
[arr[a], arr[b]] = [arr[b], arr[a]];
update(a);
update(b);
}
function bubbleSort(arr) {
for (var i = 0; i < arr.length; i++) {
for (var j = 0; j < (arr.length - i - 1); j++) {
sleep(delay);
if (arr[j] > arr[j + 1]) {
swap(list, j, j + 1);
}
}
}
console.log(list);
}
function shuffle(arr) {
ind = list.length - 1;
for (var i = arr.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
swap(arr, i, j);
}
}
function update(item) {
const bar = document.getElementById("bar" + item);
if (bar) {
bar.style.visibility="visible";
bar.style.height = (height / size) * list[item] + "px";
}
}
function createBars(arr) {
count = 0;
while (count < arr.length) {
var bar = document.createElement('div');
bar.classList.add("bars");
bar.setAttribute('id', "bar" + count);
document.body.appendChild(bar);
bar.style.width = Math.round(width / size) + "px";
bar.style.visibility = "visible";
bar.style.bottom = "0%";
bar.style.left = count * width / size + "px";
bar.style.height = (height / size) * arr[count] + "px";
count++;
updateAll();
}
}
function gen() {
if (list != undefined) {
var element = document.getElementById("parent");
element.textContent = "";
}
list = [];
size = document.getElementById("Size").value;
for (count = 1; count <= size; count++) {
list.push(count);
}
shuffle(list);
createBars(list);
sleep(1000);
sort = document.getElementById("algorithm").value;
delay = document.getElementById("delay").value;
if (sort == "bubble") {
bubbleSort(list);
}
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
function updateAll() {
for (let i = 0; i < list.length; i++) {
update(list, i);
}
}
.bars{
background-color: black;
width: 10px;
height: 10px;
position: absolute;
bottom: 100%;
margin: 0px;
visibility: hidden;
}
<div class="controls">
<select id="algorithm">
<option value="bubble">Bubble Sort</option>
<option value="selection">Selection Sort</option>
</select>
<input type="text" id="Size" value=10>
<button onclick="gen()">Generate</button>
<input type="number" id="delay" min="0" value="1"> (ms delay)
</div>
<div class="bars" id="setup"></div>
Sorry about the length.
What I expected to happen was that the bars would show up after the createBars() function was done, but this did not happen. I expected to be able to see the bars changing height to match the corresponding item of the "list" array. This didn’t happen. does anyone know a possible fix for the problem?
2
Answers
Javascript is a single threadded environment (with some out of scope exceptions). This means that the same thread that executes your js is also used to render html. This single thread needs to be released at certain points in order to allow visual changes to happen
The
sleep
method in this code actually does the opposite of sleep. It ties the thread up doing unnecessary calculations in a loopOne way to achieve a better
sleep
is to usesetTimeout
, promises andasync
/await
The sleep method will return a promise immediately and the calling code can
await
the promise. Theawait
time will be themilliseconds
time. The benefit ofawait
is that it releases the single thread so that the UI can start to renderHere is an example of how to use the new
sleep
async
andawait
are contagious. This means that every function that uses anasync
function will itself need to be changed to anasync
also. So a lot of the code will need to be asyncifiedasync
/await
can be a little difficult to understand at first, so I would recommend doing some reading on the subject if you are having difficultieshttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
The sisue is that, as I mentioned in the comments, your sleep function is blocking the UI, which means that javascript won’t do anything else while it’s waiting, including re-draw the UI.
Instead, you need to use an asynchronous function, which will allow the UI to render while it’s waiting.
You’ll also want to change the default delay to something more perceptible. A single millisecond will barely be noticible. Keep in mind that there are 1000 milliseconds in a second, so try something like 200 and see how that looks.