Since asynchronous tasks can be executed in a single thread, we need a way to execute all tasks, even if some tasks generate new tasks during execution. There are two approaches to run all tasks:
- Run futures and collect streams directly with blocking
- Use an executor to run futures and streams
Let's explore them both in the following sections.