Jesse G
Jesse G

Reputation: 538

How can I have one function run only when the other has finished ? (setTimeout within for-loop)

I've trying to find an easy, straightforward way to get the second function to run after the first one finishes. I've tried some suggestions I've come across online but these particular functions seem to operate differently due to the fact that they have setTimeouts within for-loops. Everything I've tried so far causes the second function to run before the first one is finished doing its thing (which is to update some color styling on the page).

const [VisitedNodes, setVisitedNodes] = useState([]);  
const [ShortestPath, setShortestPath] = useState([]);

// runs first
function drawVisitedNodes() {
  for (let i = 0; i < VisitedNodes.length; i++) {
    const node = VisitedNodes[i];
    setTimeout(() => {
        document.getElementById(`${node.x}-${node.y}`).className =
          "node-visited";
    }, i);
  }
  drawShortestPath();
}

// runs second
function drawShortestPath() {
  for (let i = 0; i < ShortestPath.length; i++) {
    const node = ShortestPath[i];
    setTimeout(() => {
        document.getElementById(`${node.x}-${node.y}`).className =
          "node-shortPath";

    }, i * 10);
  }
}

<button 
    onClick={drawVisitedNodes}>Visualize
</button>

Upvotes: 0

Views: 55

Answers (2)

Tom
Tom

Reputation: 338

You can try something like this:

const [VisitedNodes, setVisitedNodes] = useState([]);  
const [ShortestPath, setShortestPath] = useState([]);

// runs first
function drawVisitedNodes() {
  let x= 0;
  for (let i = 0; i < VisitedNodes.length; i++) {
    const node = VisitedNodes[i];
    setTimeout(() => {
        document.getElementById(`${node.x}-${node.y}`).className =
          "node-visited";
    }, i);
    x++;
  }
  setTimeout(() =>  drawShortestPath(), x);
}

// runs second
function drawShortestPath() {
  for (let i = 0; i < ShortestPath.length; i++) {
    const node = ShortestPath[i];
    setTimeout(() => {
        document.getElementById(`${node.x}-${node.y}`).className =
          "node-shortPath";

    }, i * 10);
  }
}

<button 
    onClick={drawVisitedNodes}>Visualize
</button>

This will add some delay to the execution of the second function. How much time? The x will tell you, because at the end of the for it will be VisitedNodes.length. Yes, you can also do it like this:

setTimeout(() =>  drawShortestPath(), VisitedNodes.length)

So, just to give you an idea of why this works, I’m gonna give you the next example:

  • VisitedNodes state is an array of 4 elements.
  • ShortestPath state is an array of 2 elements.
  • Click on the button will cause the execution of the drawVisitedNodes() function.

In your case, the execution will be:

  1. setTimeout(() => codeForNode1, 0)
  2. setTimeout(() => codeForNode2, 1)
  3. setTimeout(() => codeForNode3, 2)
  4. setTimeout(() => codeForNode4, 3)
  5. drawShortestPath()

But, because of the setTimeout’s, the actual execution will be:

  1. drawShortestPath()
  2. codeForNode1
  3. codeForNode2
  4. codeForNode3
  5. codeForNode4

With what I recommend you, your code will keep the order. Let’s see:

  1. setTimeout(() => codeForNode1, 0)
  2. setTimeout(() => codeForNode2, 1)
  3. setTimeout(() => codeForNode3, 2)
  4. setTimeout(() => codeForNode4, 3)
  5. setTimeout(() => drawShortestPath(), 4)

The final execution will be:

  1. codeForNode1
  2. codeForNode2
  3. codeForNode3
  4. codeForNode4
  5. drawShortestPath()

In the comments, you asked me why using an actual number doesn’t work. Well, my answer is that it depends on the amount of VisitedNodes that you have. Using the example I just gave you, if you use x < 4 it won’t work, because you are trying to execute the drawShortestPath() function before codeForNode4 gets executed, so you are mixing the executions in that case. But if you set an x ≥ 4 it will work as you expect.

Upvotes: 1

Khaled Ahmed
Khaled Ahmed

Reputation: 1134

you can use await to wait till all the logic done

like this

async function foo() {
    for (let i = 0; i < VisitedNodes.length; i++) {
        const node = VisitedNodes[i];
        setTimeout(() => {
            document.getElementById(`${node.x}-${node.y}`).className =
              "node-visited";
        }, i);
    }
}

function drawVisitedNodes() {
  await foo();
  drawShortestPath();
}

// runs second
function drawShortestPath() {
  for (let i = 0; i < ShortestPath.length; i++) {
    const node = ShortestPath[i];
    setTimeout(() => {
        document.getElementById(`${node.x}-${node.y}`).className =
          "node-shortPath";

    }, i * 10);
  }
}

Upvotes: 0

Related Questions