1

Im trying to create a Sort Algorithm Visualizer in React. This function is working but I want to slow down the for-loop so the state sets every 400 milliseconds.

bubbleSort = (arr) => {
        console.log('bubblesort is running');
        var len = arr.length;
        console.log('array length: ', len);
        console.log(arr);

        for (var i = len-1; i>=0; i--){
                 console.log("i: ", i); 
                 for(var j = 1; j<=i; j++){
                     console.log("j: ", j);
                     if(arr[j-1]>arr[j]){
                        var temp = arr[j-1];
                        arr[j-1] = arr[j];
                        arr[j] = temp;
                        console.log("current array: ", arr);
                        this.setState({
                            array: arr
                        })
                     }
                 }
            } 
            console.log("final array: ", arr)
            return arr
     }
William
  • 11
  • 1
  • you can't, not without completely re-writing the code. JavaScript is (effectively) single threaded, so you have to chunk your code up into smaller units so as to yield control to the event loop and other code that might be wanting to display that state. – Alnitak Feb 21 '20 at 09:38
  • Does this answer your question? [Sleep in JavaScript - delay between actions](https://stackoverflow.com/questions/758688/sleep-in-javascript-delay-between-actions) – Julien Feb 21 '20 at 09:38
  • @Alnitak You can, now. `async`/`await` allows you to do it without a rewrite. Writing an answer now... – Amadan Feb 21 '20 at 09:38
  • @Amadan I expect that's pretty close to doing a complete re-write, especially since there's no function calls in the OPs code that you can `await`. – Alnitak Feb 21 '20 at 09:39
  • You can use `await` inside a standard `for` loop, which will "stagger" the iteration of the loop. – Terry Feb 21 '20 at 09:40
  • @Alnitak See below. Two lines inserted, one line changed, I wouldn't say it's a complete rewrite. – Amadan Feb 21 '20 at 09:43

1 Answers1

6

The easiest way is to use async/await paradigm.

Make a function that implements a delay (by returning a promise that resolves after a timeout):

const delay = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));

Then convert your function into an async function:

bubbleSort = async (arr) => {

Finally, stick the delay call into your loop:

await delay(400);

EDIT: Per comments, the use of setTimeout means that it is impossible for the function to return the result directly, since the result will not be available at the time the function exits. If you only care about visualising the sort, the above will be sufficient. If you actually want the sorted array, you will have to await your bubbleSort function (or chain the promise it returns):

// needs to be inside `async` function
let sortedArray = await bubbleSort(array);

or

// the result needs to be used in callback
bubbleSort(array).then(sortedArray => ... );
Amadan
  • 191,408
  • 23
  • 240
  • 301
  • Fair enough, although the _caller_ of this function now needs to know to `await` to get the result, which means either that function has to be `async` or consume the Promise directly so that it knows when the sort has been completed – Alnitak Feb 21 '20 at 09:49
  • @Alnitak Correct. It is impossible to slow down a _synchronous_ function. There's no way to insert a delay and have the function still return a meaningful result. This remains true whether you use `setTimeout`, `Promise` or `async`/`await` paradigm. I guess I should note that in the answer... – Amadan Feb 21 '20 at 09:52
  • `async` / `await` can be viral - once you add them somewhere down the call stack you tend to have to propagate that all the way up – Alnitak Feb 21 '20 at 09:52