HORIZON HASKELLDocslts/ghc-9.10.x248f8f02026-10-05Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · 248f8f0 · 2026-10-05

Moduleasync-2.2.5Haskell2010

Control.Concurrent.Async

This module provides a set of operations for running IO operations asynchronously and waiting for their results. It is a thin layer over the basic concurrency operations provided by Control.Concurrent. The main additional functionality it provides is the ability to wait for the return value of a thread, but the interface also provides some additional safety and robustness over using forkIO threads and MVar directly.

High-level API

async's high-level API spawns lexically scoped threads, ensuring the following key poperties that make it safer to use than using plain forkIO:

  1. No exception is swallowed (waiting for results propagates exceptions).

  2. No thread is leaked (left running unintentionally).

(This is done using the bracket pattern to work in presence of synchronous and asynchronous exceptions.)

Most practical/production code should only use the high-level API.

The basic type is Async a, which represents an asynchronous IO action that will return a value of type a, or die with an exception. An Async is a wrapper around a low-level forkIO thread.

The fundamental function to spawn threads with the high-level API is withAsync.

For example, to fetch two web pages at the same time, we could do this (assuming a suitable getURL function):

withAsync (getURL url1) $ \a1 -> do
  withAsync (getURL url2) $ \a2 -> do
    page1 <- wait a1
    page2 <- wait a2
    ...

where withAsync starts the operation in a separate thread, and wait waits for and returns the result.

  • If the operation throws an exception, then that exception is re-thrown by wait. This ensures property (1): No exception is swallowed.

  • If an exception bubbles up through a withAsync, then the Async it spawned is canceled. This ensures property (2): No thread is leaked.

Often we do not care to work manually with Async handles like a1 and a2. Instead, we want to express high-level objectives like performing two or more tasks concurrently, and waiting for one or all of them to finish.

For example, the pattern of performing two IO actions concurrently and waiting for both their results is packaged up in a combinator concurrently, so we can further shorten the above example to:

(page1, page2) <- concurrently (getURL url1) (getURL url2)
...

The section High-level utilities covers the most common high-level objectives, including:

Click here to scroll to that section: Control.Concurrent.Async#high-level-utilities.

Low-level API

Some use cases require parallelism that is not lexically scoped.

For those, the low-level function async can be used as a direct equivalent of forkIO:

-- Do NOT use this code in production, it has a flaw (explained below).
do
  a1 <- async (getURL url1)
  a2 <- async (getURL url2)
  page1 <- wait a1
  page2 <- wait a2
  ...

In contrast to withAsync, this code has a problem.

It still fulfills property (1) in that an exception arising from getUrl will be re-thrown by wait, but it does not fulfill property (2). Consider the case when the first wait throws an exception; then the second wait will not happen, and the second async may be left running in the background, possibly indefinitely.

withAsync is like async, except that the Async is automatically killed (using uninterruptibleCancel) if the enclosing IO operation returns before it has completed. Furthermore, withAsync allows a tree of threads to be built, such that children are automatically killed if their parents die for any reason.

If you need to use the low-level API, ensure that you guarantee property (2) by other means, such as linking asyncs that need to die together, and protecting against asynchronous exceptions using bracket, mask, or other functions from Control.Exception.

Miscellaneous

The Functor instance can be used to change the result of an Async. For example:

ghci> withAsync (return 3) (\a -> wait (fmap (+1) a))
4
Resource exhaustion

As with all concurrent programming, keep in mind that while Haskell's cooperative ("green") multithreading carries low overhead, spawning too many of them at the same time may lead to resource exhaustion (of memory, file descriptors, or other limited resources), given that the actions running in the threads consume these resources.

  • 5 types
  • 53 values
  • Packageasync-2.2.5
  • Exports58
  • LanguageHaskell2010
  • LicenceBSD-3-Clause
  • SourceAsync.hs

Asynchronous actions

1 declaration
datadata Async a
#

An asynchronous action spawned by async or withAsync. Asynchronous actions are executed in a separate thread, and operations are provided for waiting for asynchronous actions to complete and obtaining their results (see e.g. wait).

Instances4Functor, Eq, Ord, Hashable
  • Functor AsyncDefined in async-2.2.5 · Control.Concurrent.Async.Internal
  • Eq (Async a)Defined in async-2.2.5 · Control.Concurrent.Async.Internal
  • Ord (Async a)Defined in async-2.2.5 · Control.Concurrent.Async.Internal
  • Hashable (Async a)Defined in async-2.2.5 · Control.Concurrent.Async.Internal

High-level API

0 declarations

Spawning with automatic cancelation

valuewithAsync :: IO a -> (Async a -> IO b) -> IO b
#

Spawn an asynchronous action in a separate thread, and pass its Async handle to the supplied function. When the function returns or throws an exception, uninterruptibleCancel is called on the Async.

withAsync action inner = mask $ \restore -> do
  a <- async (restore action)
  restore (inner a) `finally` uninterruptibleCancel a

This is a useful variant of async that ensures an Async is never left running unintentionally.

Note: a reference to the child thread is kept alive until the call to withAsync returns, so nesting many withAsync calls requires linear memory.

Querying Asyncs

valuewait :: Async a -> IO a
#

Wait for an asynchronous action to complete, and return its value. If the asynchronous action threw an exception, then the exception is re-thrown by wait.

wait = atomically . waitSTM
valuepoll :: Async a -> IO (Maybe (Either SomeException a))
#

Check whether an Async has completed yet. If it has not completed yet, then the result is Nothing, otherwise the result is Just e where e is Left x if the Async raised an exception x, or Right a if it returned a value a.

poll = atomically . pollSTM
valuewaitCatch :: Async a -> IO (Either SomeException a)
#

Wait for an asynchronous action to complete, and return either Left e if the action raised an exception e, or Right a if it returned a value a.

waitCatch = atomically . waitCatchSTM
valuecancel :: Async a -> IO ()
#

Cancel an asynchronous action by throwing the AsyncCancelled exception to it, and waiting for the Async thread to quit. Has no effect if the Async has already completed.

cancel a = throwTo (asyncThreadId a) AsyncCancelled <* waitCatch a

Note that cancel will not terminate until the thread the Async refers to has terminated. This means that cancel will block for as long said thread blocks when receiving an asynchronous exception.

For example, it could block if:

  • It's executing a foreign call, and thus cannot receive the asynchronous exception;

  • It's executing some cleanup handler after having received the exception, and the handler is blocking.

valuecancelMany :: [Async a] -> IO ()
#

Cancel multiple asynchronous actions by throwing the AsyncCancelled exception to each of them in turn, then waiting for all the Async threads to complete.

valuecancelWith :: Exception e => Async a -> e -> IO ()
#

Cancel an asynchronous action by throwing the supplied exception to it.

cancelWith a x = throwTo (asyncThreadId a) x

The notes about the synchronous nature of cancel also apply to cancelWith.

High-level utilities

valuerace :: IO a -> IO b -> IO (Either a b)
#

Run two IO actions concurrently, and return the first to finish. The loser of the race is cancelled.

race left right =
  withAsync left $ \a ->
  withAsync right $ \b ->
  waitEither a b
valueconcurrently :: IO a -> IO b -> IO (a, b)
#

Run two IO actions concurrently, and return both results. If either action throws an exception at any time, then the other action is cancelled, and the exception is re-thrown by concurrently.

concurrently left right =
  withAsync left $ \a ->
  withAsync right $ \b ->
  waitBoth a b
valuemapConcurrently :: Traversable t => (a -> IO b) -> t a -> IO (t b)
#

Maps an IO-performing function over any Traversable data type, performing all the IO actions concurrently, and returning the original data structure with the arguments replaced by the results.

If any of the actions throw an exception, then all other actions are cancelled and the exception is re-thrown.

For example, mapConcurrently works with lists:

pages <- mapConcurrently getURL ["url1", "url2", "url3"]

Take into account that async will try to immediately spawn a thread for each element of the Traversable, so running this on large inputs without care may lead to resource exhaustion (of memory, file descriptors, or other limited resources).

newtypenewtype Concurrently a
#

A value of type Concurrently a is an IO operation that can be composed with other Concurrently values, using the Applicative and Alternative instances.

Calling runConcurrently on a value of type Concurrently a will execute the IO operations it contains concurrently, before delivering the result of type a.

For example

(page1, page2, page3)
    <- runConcurrently $ (,,)
    <$> Concurrently (getURL "url1")
    <*> Concurrently (getURL "url2")
    <*> Concurrently (getURL "url3")
Instances5Functor, Applicative, Alternative, Semigroup, Monoid
valueconcurrentlyE :: IO (Either e a) -> IO (Either e b) -> IO (Either e (a, b))
#

Run two IO actions concurrently. If both of them end with Right, return both results. If one of then ends with Left, interrupt the other action and return the Left.

newtypenewtype ConcurrentlyE e a
#

A value of type ConcurrentlyE e a is an IO operation that can be composed with other ConcurrentlyE values, using the Applicative instance.

Calling runConcurrentlyE on a value of type ConcurrentlyE e a will execute the IO operations it contains concurrently, before delivering either the result of type a, or an error of type e if one of the actions returns Left.

| @since 2.2.5

Instances5Bifunctor, Functor, Applicative, Semigroup, Monoid

Specialised operations

STM operations

Waiting for multiple Asyncs

valuewaitAny :: [Async a] -> IO (Async a, a)
#

Wait for any of the supplied Asyncs to complete. If the first to complete throws an exception, then that exception is re-thrown by waitAny. The input list must be non-empty.

If multiple Asyncs complete or have completed, then the value returned corresponds to the first completed Async in the list.

valuewaitAnyCatch :: [Async a] -> IO (Async a, Either SomeException a)
#

Wait for any of the supplied asynchronous operations to complete. The value returned is a pair of the Async that completed, and the result that would be returned by wait on that Async. The input list must be non-empty.

If multiple Asyncs complete or have completed, then the value returned corresponds to the first completed Async in the list.

valuewaitEither :: Async a -> Async b -> IO (Either a b)
#

Wait for the first of two Asyncs to finish. If the Async that finished first raised an exception, then the exception is re-thrown by waitEither.

valuewaitBoth :: Async a -> Async b -> IO (a, b)
#

Waits for both Asyncs to finish, but if either of them throws an exception before they have both finished, then the exception is re-thrown by waitBoth.

Waiting for multiple Asyncs in STM

Low-level API

0 declarations

Spawning (low-level API)

valueasync :: IO a -> IO (Async a)
#

Spawn an asynchronous action in a separate thread.

Like for forkIO, the action may be left running unintentionally (see module-level documentation for details).

Use withAsync style functions wherever you can instead!

Linking

valuelinkOnly
  1. :: (SomeException -> Bool)

    return True if the exception should be propagated, False otherwise.

  2. -> Async a
  3. -> IO ()
#

Link the given Async to the current thread, such that if the Async raises an exception, that exception will be re-thrown in the current thread, wrapped in ExceptionInLinkedThread.

The supplied predicate determines which exceptions in the target thread should be propagated to the source thread.

valuelink2Only :: (SomeException -> Bool) -> Async a -> Async b -> IO ()
#

Link two Asyncs together, such that if either raises an exception, the same exception is re-thrown in the other Async, wrapped in ExceptionInLinkedThread.

The supplied predicate determines which exceptions in the target thread should be propagated to the source thread.