skip to Main Content

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


  1. 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 loop

    One way to achieve a better sleep is to use setTimeout, promises and async/await

    // create a promise that will "resolve" after milliseconds
    function sleep(milliseconds) {
      return new Promise(resolve => {
        setTimeout(resolve, milliseconds)
      })
    }
    

    The sleep method will return a promise immediately and the calling code can await the promise. The await time will be the milliseconds time. The benefit of await is that it releases the single thread so that the UI can start to render

    Here is an example of how to use the new sleep

    // enable the await keyword by making this function async
    async function bubbleSort(arr) {
      for (var i = 0; i < arr.length; i++) {
        for (var j = 0; j < (arr.length - i - 1); j++) {
          // await the sleep method
          await sleep(delay);
          if (arr[j] > arr[j + 1]) {
            swap(list, j, j + 1);
          }
        }
      }
      console.log(list);
    }
    

    async and await are contagious. This means that every function that uses an async function will itself need to be changed to an async also. So a lot of the code will need to be asyncified

    async/await can be a little difficult to understand at first, so I would recommend doing some reading on the subject if you are having difficulties

    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

    Login or Signup to reply.
  2. 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.

    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);
    }
    
    async function bubbleSort(arr) {
      for (var i = 0; i < arr.length; i++) {
        for (var j = 0; j < (arr.length - i - 1); j++) {
          await 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) {
      return new Promise(d=>setTimeout(d, milliseconds));
    }
    
    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="100" value="200"> (ms delay)
        </div>
            <div class="bars" id="setup"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search