```
import asyncio
from typing import Any

import asyncio
import threading
from typing import Callable, Optional, Any

class ThreadWorker:
    def __init__(self):
        """
        Initializes the ThreadWorker by starting a new thread
        that runs an asyncio event loop.
        """
        self.loop = None
        self.loop_started = threading.Event()  # Event to signal loop initialization
        self.thread = threading.Thread(target=self.loop_task, daemon=True)
        self.thread.start()

        # Wait until the loop is initialized
        loop_initialized = self.loop_started.wait(timeout=5)  # Timeout to prevent indefinite blocking
        if not loop_initialized:
            raise RuntimeError("Failed to initialize the event loop within the timeout period.")

    def loop_task(self):
        """Starts the asyncio event loop in a separate thread."""
        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)
        self.loop_started.set()  # Signal that the loop is initialized
        self.loop.run_forever()

    def after(
        self,
        func: Callable[..., Any],
        callback: Optional[Callable[[Any], None]] = None,
        delay: Optional[float] = None,
        args: Optional[Any] = None
    ):
        """
        Schedules a function or coroutine to be executed in the event loop.

        :param func: The function or coroutine to execute.
        :param callback: Optional function to call with the result.
        :param delay: Optional delay in seconds before execution.
        :param args: Optional positional arguments to pass to `func`.
        """
        if args is None:
            args = ()

        async def task():
            if delay:
                await asyncio.sleep(delay)
            if asyncio.iscoroutinefunction(func):
                result = await func(*args)
            else:
                result = func(*args)
            if callback:
                if asyncio.iscoroutinefunction(callback):
                    await callback(result)
                else:
                    # If callback is a regular function, run it in the default executor
                    loop = asyncio.get_running_loop()
                    await loop.run_in_executor(None, callback, result)

        # Schedule the coroutine to run in the event loop thread
        asyncio.run_coroutine_threadsafe(task(), self.loop)

    async def after_async(
        self,
        func: Callable[..., Any],
        delay: Optional[float] = None,
        args: Optional[Any] = None
    ) -> Any:
        """
        Asynchronously schedules a function or coroutine to be executed in the event loop
        and awaits its result.

        :param func: The function or coroutine to execute.
        :param delay: Optional delay in seconds before execution.
        :param args: Optional positional arguments to pass to `func`.
        :return: The result of `func`.
        """
        if args is None:
            args = ()

        if delay:
            await asyncio.sleep(delay)

        if asyncio.iscoroutinefunction(func):
            return await func(*args)
        else:
            loop = asyncio.get_running_loop()
            return await loop.run_in_executor(None, func, *args)

    def stop(self):
        """
        Stops the event loop
        """
        if self.loop and self.loop.is_running():
            self.loop.call_soon_threadsafe(self.loop.stop)

    def join(self, timeout: Optional[float] = None):
        """
        Waits for the worker thread to finish.

        :param timeout: Optional timeout in seconds.
        """
        self.thread.join(timeout)

    def __del__(self):
        """
        Ensures the event loop is stopped when the ThreadWorker is garbage collected.
        """
        self.stop()

# Assuming ThreadWorker is already defined as above

def task1(a, b, c):
    print(f"[task1] Executed with arguments: {a}, {b}, {c}")
    return f"Result from task1 with args ({a}, {b}, {c})"


async def task2():
    print("[task2] Started")
    await asyncio.sleep(1)
    print("[task2] Completed")
    return "Result from task2"


async def task3(worker: ThreadWorker):
    print("[task3] Awaiting task1 with 2-second delay")
    result = await worker.after_async(task1, delay=2, args=(1, 2, 3))
    print(f"[task3] Received: {result}")

    await asyncio.sleep(1)
    print("[task3] Printing 'Foo'")
    print("Foo")

    print("[task3] Stopping worker")
    worker.stop()


def callback(result: Any):
    print(f"[callback] Received: {result}")


# Initialize the worker
worker = ThreadWorker()

# Schedule various tasks
worker.after(task1, args=(10, 20, 30))  # Synchronous task without callback
worker.after(task2, callback=callback)  # Asynchronous task with callback
worker.after(task1, callback=callback, delay=3, args=(40, 50, 60))  # Synchronous task with callback and delay
worker.after(task1, args=(70, 80, 90))  # Another synchronous task without callback

# Schedule an asynchronous task that utilizes after_async
worker.after(task3, args=(worker,))

# Wait for the worker thread to finish
worker.join()
print("Worker has been stopped.")
```

help improve the `stop()` method so that it only terminates the loop when it has no pending tasks.
the current implementation
```
    def stop(self):
        """
        Stops the event loop
        """
        if self.loop and self.loop.is_running():
            self.loop.call_soon_threadsafe(self.loop.stop)
```

inserts a task to stop the loop but neglects tasks coming after it, which is not desirable