top of page

The 3 methods in concurrent.futures

Updated: Apr 9

The map method confuses me, so I typically use the submit method. I am hoping ChatGPT helps me understand it better, so I know when to use it. Help me, ChatGPT... After reading what is below i know why I use submit -- it returns results as they are ready -- combined with as_completed. On the other hand, order matters in map and it returns results only when all are ready. I guess you would use map in a workflow when you can go to the next step only when all processes in this step are done...


Let's dive deeper into the three main methods you use with concurrent.futures executors:

  • .submit()

  • .map()

  • .as_completed()

Each has its strengths depending on how you want to run and manage parallel tasks — and they all work with both ThreadPoolExecutor and ProcessPoolExecutor.


✅ 1. .submit()

🔹 What it does:

  • Submits a single task to the executor and returns a Future object.

  • You can use .submit() multiple times to queue up several tasks.

🔧 Syntax:

future = executor.submit(function, arg1, arg2, ...)

📌 Features:

  • Allows fine-grained control over each task

  • Returns immediately with a Future object

  • You can later use .result() to get the result

🧪 Example:

from concurrent.futures import ThreadPoolExecutor

def square(x):
    return x * x

with ThreadPoolExecutor() as executor:
    futures = [executor.submit(square, i) for i in range(5)]
    for future in futures:
        print(future.result())

✅ 2. .map()

🔹 What it does:

  • Submits multiple tasks at once by applying a function to a sequence of inputs (like map()).

  • Returns the results in the original order.

🔧 Syntax:

results = executor.map(function, iterable)

📌 Features:

  • Easy one-liner for mapping a function over multiple inputs

  • Automatically blocks until all tasks are done

  • No access to individual Future objects

🧪 Example:

from concurrent.futures import ProcessPoolExecutor

def cube(x):
    return x ** 3

with ProcessPoolExecutor() as executor:
    results = executor.map(cube, [1, 2, 3, 4])
    print(list(results))  # Output: [1, 8, 27, 64]

✅ 3. .as_completed()

🔹 What it does:

  • Works with a list of Future objects (usually from .submit()).

  • Yields each future as soon as it finishes, not in the order submitted.

🔧 Syntax:

from concurrent.futures import as_completed
for future in as_completed(futures):
    result = future.result()

📌 Features:

  • Ideal for cases where you want results as soon as they’re ready

  • Useful for long-running tasks where early results can be processed immediately

  • Better performance for streaming/interactive systems

🧪 Example:

from concurrent.futures import ThreadPoolExecutor, as_completed
import time
import random

def task(x):
    sleep_time = random.uniform(0.5, 2.0)
    time.sleep(sleep_time)
    return f"Task {x} completed in {sleep_time:.2f} sec"

with ThreadPoolExecutor() as executor:
    futures = [executor.submit(task, i) for i in range(5)]
    for future in as_completed(futures):
        print(future.result())

🧾 Summary Comparison

Feature

.submit()

.map()

as_completed()

Returns Futures?

✅ Yes

❌ No (returns results)

✅ Works with Futures

Order of results

You control the order

Preserves input order

Order based on task completion

Use case

Full control, custom logic

Quick batch processing

Early result processing

Can handle args?

✅ Yes (any args)

✅ Yes (1 iterable per param)

✅ Yes (via submit)


🧠 When to Use Each

  • Use .submit() when:

    • You need flexibility

    • You want to manage each task individually

    • You need to cancel or monitor specific tasks

  • Use .map() when:

    • You want a quick, clean way to apply a function to a list of inputs

    • You don't need to manage tasks individually

  • Use as_completed() when:

    • You want to handle results as soon as they’re ready

    • Tasks vary in execution time

    • You’re building responsive or streaming workflows



📊 concurrent.futures Execution Flow


┌─────────────────────────────┐

│ Executor (with)                                          │

│ ThreadPool or ProcessPool                          │

└─────────────────┬───────────┘

                                                                    │

┌────────────────────┼────────────────────┐

│                                                    │                                                   │

.submit(fn, arg)                          .map(fn, iterable)       .submit(...) + as_completed()

│                                               │                                │

┌───────▼────────────┐ ┌────▼─────┐ ┌──────────▼────┐

│ Returns Future objects │ │ Executes fn(x) │ │ Returns  Future as     │

│ for each task                               │ │ for each item │ │ soon as it's done      │

└────────────────────┘ └──────────┘ └───────────────┘

                                             │

                         ┌───────▼────────┐

                                                       │ Results returned in │

                                                       │ original input order          │

                                                       └────────────────┘

Recent Posts

See All

Comments


© 2020 by Silvia Mazzoni, Silvia's Brainery, Santa Monica, CA

bottom of page