Reputation: 63
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
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
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