Simple way to wait for an executing function to finish before executing another function in Javascript
Tags: webdev, javascript
Published: 2021-09-25 12:45:05

Actually, I'm not really sure about the title of this blog because currently, I can't come up with any better title to describe what I want to share with you guys today. I don't know if it's called something else, if anyone knows, please point it out, I very much appreciate it. 

Okay, so first let's start with our use case: 

We have 2 functions, let's call them A and B. We call them at the same time, but we want B to wait for A to finish first. 

 I have a really simple function like this: 

```jsx
(async () => {
    const sub = {
        name: "sub",
        subscribed: false,
    };

    // 1.subscribe function
    const subscribe = () => {
        setTimeout(() => {
            sub.subscribed = true;
        }, [9.5 * 1000]); // execute after 9.5 seconds
    };

    // 2. a function only happen after subscribed
    const afterSubscribed = () => {
        if (sub.subscribed) {
            console.log("subscribed");
        } else {
            console.log("Not subscribed");
        }
    };

    subscribe();
    afterSubscribed();
})();
```

And the output of this function would be: 

```jsx
// immediately
Not subscribed
// Continue to run 9.5 seconds then stop
```

The result I want is that this function somehow prints out "Subscribed". 

Let's try to solve it. 

The first try we can do is that we can create an interval in the afterSubscribed  function like this, note that we will add a timer to see when we get right log: 

```jsx
// 2. a function only happen after subscribed
    const afterSubscribed = async () => {
        const start = Date.now();
        const interval = setInterval(() => {
            if (sub.subscribed) {
                console.log("subscribed");
                const end = Date.now();
                const duration = Math.floor(end - start);
                console.log(`on: ${duration}`);
                clearInterval(interval);
            } else {
                console.log("Not subscribed");
            }
        }, [1000]);
    };
```

Now we will retrieve the result we want: 

```bash
// This is the log of this function
Not subscribed
Not subscribed
Not subscribed
Not subscribed
Not subscribed
Not subscribed
Not subscribed
Not subscribed
Not subscribed
subscribed
on: 10011
```

Yeah, it prints out the result we want, it's quite good. The issue of this approach is that we only check for the state of the sub every 1 second. So in case, our subscribe function finishes after 9.1, 9.2... seconds, we still have to wait until the 10th second. But it's still acceptable as long as we don't need afterSubscribed to continue to execute right after subscribe finished. 

To resolve the issue of the #1 try, we can change our functions like this: 

```jsx
(async () => {
    const sub = {
        name: "sub",
        subscribed: false,
        doneSubscribed: false,
        processSubscribe: false,
    };

    // 1.subscribe function
    const subscribe = () => {
        sub.processSubscribe = new Promise(
            (resolve) => (sub.doneSubscribed = resolve)
        );

        setTimeout(() => {
            sub.subscribed = true;
            sub.doneSubscribed();
        }, [9.5 * 1000]); // execute after 9.5 seconds
    };

    // 2. a function only happen after subscribed
    const afterSubscribed = async () => {
        const start = Date.now();
        await sub.processSubscribe;
        if (sub.subscribed) {
            console.log("subscribed");
        } else {
            console.log("Not subscribed");
        }
        const end = Date.now();
        const duration = Math.floor(end - start);
        console.log(`on: ${duration}`);
    };
    subscribe();
    afterSubscribed();
})();
```

 And this is what we get: 

```jsx
// Wait for 9.5 second then..
subscribed
on: 9507
```

Okay, so no more "Not subscribed" and right after subscribe finished its works

Let me explain how it works. 

We add 2 more attributes to sub : 

```jsx
doneSubscribed: false,
processSubscribe: false,
```

And in subscribe function, we assign sub.processSubscribe to a promise where resolve function is assigned to sub.doneSubscribe. In setTimeout, we call sub.doneSubscribe function (since we assigned it to the resolve function of sub.processSubscribe promise, it's a function now). The trick here is that we assign the resolve function of sub.processSubscribe to sub.doneSubscribe, we all know that the promise resolves when its resolve/reject function is called. By awaiting for sub.processSubscribe we also wait for setTimeout to finish as well as subscribe function.

Of course, there might be some other way to solve this problem, but I think this one is one of the shortest and best ways to solve it.  

So in general, this problem can be described as "wait for an executing function to finish before executing another function". 

If you guys have any other ways to solve it. Feel free to share with me. Or If I did any mistake, please point it out, I really appreciate it. Thank for reading