lord404
lord404

Reputation: 63

JavaScript run one function after another

I am trying to create a visual selection sort. finMin() will go through the array one by one displaying the new min when found. I want to use this function in a loop for selection sort. If the function is run one time, then everything is fine, but if findMin() is run in a loop, then the function has bugs.

If a function is run in a loop such as for(let i=0; i<3; i++){findMin();} does the second iteration of the loop run immediately or does it wait for findMin to return before i == 1? I believe that this loop should be sequential, but I do not know why the code does not work in a loop then.

var gBars = [];
var gSelected = 19;
var gFinished = 19;
var changed = false;
var step = 0;
function Bar(index, height){
    this.index = index;
    this.height = height;

    this.getIndex = function(){
        console.log(this.index);
    };

    this.getHeight = function(){
        console.log(this.height);
    };

    this.getStats = function(){
        console.log(this.index + ' ' + this.height);
    }

    this.setHeight = function(h){
        this.height = h;
    }

    this.setIndex = function(i){
        this.index = i;
    }
}

function insertAfter(newNode, referenceNode){
    referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}

function setHeight(i, h){
    document.getElementById(i).style.height = h + 'em';
}

function addBar(i, h){
    //base case i = 0
    //first bar
    if(i === 0){
        var currentDiv = document.getElementById("root");
        d = document.createElement('div');
        d.setAttribute("id", 'block'+i);
        d.setAttribute("class", 'block');
        gBars[i] = new Bar(i, h);
        currentDiv.appendChild(d);
        setHeight('block'+i,h);
    }
    else {
        let last = i-1;
        var currentDiv = document.getElementById('block'+last);
        d = document.createElement('div');
        d.setAttribute("id", 'block'+i);
        d.setAttribute("class", 'block');
        gBars[i] = new Bar(i, h);
        insertAfter(d, currentDiv);
        setHeight('block'+i,h);
    }
}
function selSort(){
    for(let i=0; i<10; i++){
        findMin(gFinished);
    }
}

function findMin(gFinished) {
    let min = gBars[gFinished].height;
    //start at 18 because bars are rotated 180deg
    //go backwards so it appears to go forwards
    var delay = 500;
    let i = gFinished - 1;
    min = setTimeout(timeout(i, min), delay);
    return min;
}
function timeoutchange(){

    var swapped = document.getElementById('block'+gFinished);
    var selected = document.getElementById('block'+gSelected);
    let temp = gBars[gFinished].height;
    swapped.style.height = gBars[gSelected].height + 'em';
    selected.style.height = temp + 'em';
    selected.style.backgroundColor = "grey";
    var selected = document.getElementById('block'+gFinished);
    selected.style.backgroundColor = "green";
    gFinished--;
    var selected = document.getElementById('block'+gFinished);
    selected.style.backgroundColor = "blue";
    gSelected = gFinished;
    }
function timeout(i, min) {
  console.log("Next loop: " + i);
   if(i==18){
   var selected = document.getElementById('block19');
      selected.style.backgroundColor = "blue";
   }
  if(min > gBars[i].height) {
      min = gBars[i].height;
      var selected = document.getElementById('block'+i);
      selected.style.backgroundColor = "blue";
      console.log('new min ' + min);

      	 selected = document.getElementById('block'+gSelected);
	 selected.style.backgroundColor = "grey";

      gSelected = i;
  }
  i--;
  if (i == 0) {
    console.log("End");
       var swapped = document.getElementById('block'+gFinished);
       swapped.style.backgroundColor = "red";


    setTimeout(function(){
      return timeoutchange();
    },1000)



    step++;
    return min;
  } else {
    setTimeout(function(){
      return timeout(i, min);
    },500)
  }
}

function init(){
    for(let i=0; i<20; i++){
        let ran = Math.floor(Math.random() * 50 + 1);
        gBars[i] = new Bar(i,ran);
        addBar(i,ran);
    }
    for(let i=0; i<20; i++){
        gBars[i].getStats();
    }


        //works 
        findMin(gFinished);
        
        //findMin does not work in loop
        //why?
        //selSort();


    return;

}

init();
.selected{
    background-color:blue;
}

.block{
    border:1px solid rgba(0,0,0,.4);
    width:20px;
    background-color:grey;
}

#root{
    display:flex;
    transform:rotate(180deg);
    position:absolute;
    left:10%;
}
<html>
 <head>
     <link rel="stylesheet" href="style.css">
 </head>
 <body>
     <button>sort</button>
     <div id="root"></div>
 </body>
 <script src="selectionsort.js"></script>
</html>

Upvotes: 2

Views: 2193

Answers (2)

Chathuranga
Chathuranga

Reputation: 1036

What you want to do is use JavaScript Promises. (https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) There you have a concept called chaining so that you can chain your functions one after the other based on execution (in this case resolved). For an example Let say you have to functions:

function a() {
  setTimeout( function() {
    resolve("Success!")  // Yay! Everything went well!
  }, 250) 
}  

function b() {
  setTimeout( function() {
    resolve("Success 2!")  // Yay! Everything went well!
  }, 250) 

}

you can make these promises and chain them one after the other:

let a = new Promise((resolve, reject) => {
  setTimeout( function() {
    resolve("Success!")  // Yay! Everything went well!
  }, 250) 
}) 

let b = new Promise((resolve, reject) => {
  setTimeout( function() {
    resolve("Success2!")  // Yay! Everything went well!
  }, 250) 
})


let c = new Promise((resolve, reject) => {
  setTimeout( function() {
    resolve("Success3!")  // Yay! Everything went well!
  }, 250) 
})

a().then(()=>{
   return b();
}).then(()=>{
   return c();
}); 

Upvotes: 3

izambl
izambl

Reputation: 659

setTimeout returns a number representing the id of the timer, so when running findMin() in a loop it will return that, and immediately after execute the next iteration.

To make the loop wait for the timeout you'll have to await for a promise that is resolved after the delay

for (let i = 0; i < 3; i++) {
  min = await new Promise((resolve) => {
    setTimeout(() => {
      resolve(timeout(i, min))
    }, 500);
  })
}

Upvotes: 2

Related Questions