Futures are a complex, heterogeneous feature. Each language handles them slightly differently. Most of the discussion here is for Java.
A future has 3 parties involved in the lifecycle.
In a typical scenario, these three parties are different threads, meaning there is a high amount of coordination between them. In Java syntax:
Future<String> future = CompletableFuture.supplyAsync(() -> "a" + "b", executor); future.get();
The party that creates the CompletableFuture object is the Scheduler. It is the current thread. The Fulfiller is the executor, which is likely a ThreadPoolExecutor, or a Virtual Thread executor. It will actually be running the provided lambda, and completing the future. Finally, the current thread is acting as the Waiter. It does so by calling get(), which puts it to sleep until the Fulfiller wakes it up.