EventLoopFuture

public final class EventLoopFuture<Value>

Holder for a result that will be provided later.

Functions that promise to do work asynchronously can return an EventLoopFuture<Value>. The recipient of such an object can then observe it to be notified when the operation completes.

The provider of a EventLoopFuture<Value> can create and return a placeholder object before the actual result is available. For example:

func getNetworkData(args) -> EventLoopFuture<NetworkResponse> {
    let promise = eventLoop.makePromise(of: NetworkResponse.self)
    queue.async {
        . . . do some work . . .
        promise.succeed(response)
        . . . if it fails, instead . . .
        promise.fail(error)
    }
    return promise.futureResult
}

Note that this function returns immediately; the promise object will be given a value later on. This behaviour is common to Future/Promise implementations in many programming languages. If you are unfamiliar with this kind of object, the following resources may be helpful:

If you receive a EventLoopFuture<Value> from another function, you have a number of options: The most common operation is to use flatMap() or map() to add a function that will be called with the eventual result. Both methods returns a new EventLoopFuture<Value> immediately that will receive the return value from your function, but they behave differently. If you have a function that can return synchronously, the map function will transform the result of type Value to a the new result of type NewValue and return an EventLoopFuture<NewValue>.

let networkData = getNetworkData(args)

// When network data is received, convert it.
let processedResult: EventLoopFuture<Processed> = networkData.map { (n: NetworkResponse) -> Processed in
    ... parse network data ....
    return processedResult
}

If however you need to do more asynchronous processing, you can call flatMap(). The return value of the function passed to flatMap must be a new EventLoopFuture<NewValue> object: the return value of flatMap() is a new EventLoopFuture<NewValue> that will contain the eventual result of both the original operation and the subsequent one.

// When converted network data is available, begin the database operation.
let databaseResult: EventLoopFuture<DBResult> = processedResult.flatMap { (p: Processed) -> EventLoopFuture<DBResult> in
    return someDatabaseOperation(p)
}

In essence, future chains created via flatMap() provide a form of data-driven asynchronous programming that allows you to dynamically declare data dependencies for your various operations.

EventLoopFuture chains created via flatMap() are sufficient for most purposes. All of the registered functions will eventually run in order. If one of those functions throws an error, that error will bypass the remaining functions. You can use flatMapError() to handle and optionally recover from errors in the middle of a chain.

At the end of an EventLoopFuture chain, you can use whenSuccess() or whenFailure() to add an observer callback that will be invoked with the result or error at that point. (Note: If you ever find yourself invoking promise.succeed() from inside a whenSuccess() callback, you probably should use flatMap() or cascade(to:) instead.)

EventLoopFuture objects are typically obtained by:

  • Using .flatMap() on an existing future to create a new future for the next step in a series of operations.
  • Initializing an EventLoopFuture that already has a value or an error

Threading and Futures

One of the major performance advantages of NIO over something like Node.js or Python’s asyncio is that NIO will by default run multiple event loops at once, on different threads. As most network protocols do not require blocking operation, at least in their low level implementations, this provides enormous speedups on machines with many cores such as most modern servers.

However, it can present a challenge at higher levels of abstraction when coordination between those threads becomes necessary. This is usually the case whenever the events on one connection (that is, one Channel) depend on events on another one. As these Channels may be scheduled on different event loops (and so different threads) care needs to be taken to ensure that communication between the two loops is done in a thread-safe manner that avoids concurrent mutation of shared state from multiple loops at once.

The main primitives NIO provides for this use are the EventLoopPromise and EventLoopFuture. As their names suggest, these two objects are aware of event loops, and so can help manage the safety and correctness of your programs. However, understanding the exact semantics of these objects is critical to ensuring the safety of your code.

Callbacks

The most important principle of the EventLoopPromise and EventLoopFuture is this: all callbacks registered on an EventLoopFuture will execute on the thread corresponding to the event loop that created the Future, regardless of what thread succeeds or fails the corresponding EventLoopPromise.

This means that if your code created the EventLoopPromise, you can be extremely confident of what thread the callback will execute on: after all, you held the event loop in hand when you created the EventLoopPromise. However, if your code is handed an EventLoopFuture or EventLoopPromise, and you want to register callbacks on those objects, you cannot be confident that those callbacks will execute on the same EventLoop that your code does.

This presents a problem: how do you ensure thread-safety when registering callbacks on an arbitrary EventLoopFuture? The short answer is that when you are holding an EventLoopFuture, you can always obtain a new EventLoopFuture whose callbacks will execute on your event loop. You do this by calling EventLoopFuture.hop(to:). This function returns a new EventLoopFuture whose callbacks are guaranteed to fire on the provided event loop. As an added bonus, hopTo will check whether the provided EventLoopFuture was already scheduled to dispatch on the event loop in question, and avoid doing any work if that was the case.

This means that for any EventLoopFuture that your code did not create itself (via EventLoopPromise.futureResult), use of hopTo is strongly encouraged to help guarantee thread-safety. It should only be elided when thread-safety is provably not needed.

The thread affinity of EventLoopFutures is critical to writing safe, performant concurrent code without boilerplate. It allows you to avoid needing to write or use locks in your own code, instead using the natural synchronization of the EventLoop to manage your thread-safety. In general, if any of your ChannelHandlers or EventLoopFuture callbacks need to invoke a lock (either directly or in the form of DispatchQueue) this should be considered a code smell worth investigating: the EventLoop-based synchronization guarantees of EventLoopFuture should be sufficient to guarantee thread-safety.

  • Declaration

    Swift

    public static func == (lhs: EventLoopFuture, rhs: EventLoopFuture) -> Bool
  • When the current EventLoopFuture<Value> is fulfilled, run the provided callback, which will provide a new EventLoopFuture.

    This allows you to dynamically dispatch new asynchronous tasks as phases in a longer series of processing steps. Note that you can use the results of the current EventLoopFuture<Value> when determining how to dispatch the next operation.

    This works well when you have APIs that already know how to return EventLoopFutures. You can do something with the result of one and just return the next future:

    let d1 = networkRequest(args).future()
    let d2 = d1.flatMap { t -> EventLoopFuture<NewValue> in
        . . . something with t . . .
        return netWorkRequest(args)
    }
    d2.whenSuccess { u in
        NSLog("Result of second request: \(u)")
    }
    

    Note: In a sense, the EventLoopFuture<NewValue> is returned before it’s created.

    Declaration

    Swift

    @inlinable
    public func flatMap<NewValue>(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (Value) -> EventLoopFuture<NewValue>) -> EventLoopFuture<NewValue>

    Parameters

    callback

    Function that will receive the value of this EventLoopFuture and return a new EventLoopFuture.

    Return Value

    A future that will receive the eventual value.

  • When the current EventLoopFuture<Value> is fulfilled, run the provided callback, which performs a synchronous computation and returns a new value of type NewValue. The provided callback may optionally throw.

    Operations performed in flatMapThrowing should not block, or they will block the entire event loop. flatMapThrowing is intended for use when you have a data-driven function that performs a simple data transformation that can potentially error.

    If your callback function throws, the returned EventLoopFuture will error.

    Declaration

    Swift

    @inlinable
    public func flatMapThrowing<NewValue>(file: StaticString = #file,
                                line: UInt = #line,
                                _ callback: @escaping (Value) throws -> NewValue) -> EventLoopFuture<NewValue>

    Parameters

    callback

    Function that will receive the value of this EventLoopFuture and return a new value lifted into a new EventLoopFuture.

    Return Value

    A future that will receive the eventual value.

  • When the current EventLoopFuture<Value> is in an error state, run the provided callback, which may recover from the error and returns a new value of type Value. The provided callback may optionally throw, in which case the EventLoopFuture will be in a failed state with the new thrown error.

    Operations performed in flatMapErrorThrowing should not block, or they will block the entire event loop. flatMapErrorThrowing is intended for use when you have the ability to synchronously recover from errors.

    If your callback function throws, the returned EventLoopFuture will error.

    Declaration

    Swift

    @inlinable
    public func flatMapErrorThrowing(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (Error) throws -> Value) -> EventLoopFuture<Value>

    Parameters

    callback

    Function that will receive the error value of this EventLoopFuture and return a new value lifted into a new EventLoopFuture.

    Return Value

    A future that will receive the eventual value or a rethrown error.

  • When the current EventLoopFuture<Value> is fulfilled, run the provided callback, which performs a synchronous computation and returns a new value of type NewValue.

    Operations performed in map should not block, or they will block the entire event loop. map is intended for use when you have a data-driven function that performs a simple data transformation that cannot error.

    If you have a data-driven function that can throw, you should use flatMapThrowing instead.

    let future1 = eventually()
    let future2 = future1.map { T -> U in
        ... stuff ...
        return u
    }
    let future3 = future2.map { U -> V in
        ... stuff ...
        return v
    }
    

    Declaration

    Swift

    @inlinable
    public func map<NewValue>(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (Value) -> (NewValue)) -> EventLoopFuture<NewValue>

    Parameters

    callback

    Function that will receive the value of this EventLoopFuture and return a new value lifted into a new EventLoopFuture.

    Return Value

    A future that will receive the eventual value.

  • When the current EventLoopFuture<Value> is in an error state, run the provided callback, which may recover from the error by returning an EventLoopFuture<NewValue>. The callback is intended to potentially recover from the error by returning a new EventLoopFuture that will eventually contain the recovered result.

    If the callback cannot recover it should return a failed EventLoopFuture.

    Declaration

    Swift

    @inlinable
    public func flatMapError(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (Error) -> EventLoopFuture<Value>) -> EventLoopFuture<Value>

    Parameters

    callback

    Function that will receive the error value of this EventLoopFuture and return a new value lifted into a new EventLoopFuture.

    Return Value

    A future that will receive the recovered value.

  • When the current EventLoopFuture<Value> is in an error state, run the provided callback, which can recover from the error and return a new value of type Value. The provided callback may not throw, so this function should be used when the error is always recoverable.

    Operations performed in recover should not block, or they will block the entire event loop. recover is intended for use when you have the ability to synchronously recover from errors.

    Declaration

    Swift

    @inlinable
    public func recover(file: StaticString = #file, line: UInt = #line, _ callback: @escaping (Error) -> Value) -> EventLoopFuture<Value>

    Parameters

    callback

    Function that will receive the error value of this EventLoopFuture and return a new value lifted into a new EventLoopFuture.

    Return Value

    A future that will receive the recovered value.

  • Adds an observer callback to this EventLoopFuture that is called when the EventLoopFuture has a success result.

    An observer callback cannot return a value, meaning that this function cannot be chained from. If you are attempting to create a computation pipeline, consider map or flatMap. If you find yourself passing the results from this EventLoopFuture to a new EventLoopPromise in the body of this function, consider using cascade instead.

    Declaration

    Swift

    @inlinable
    public func whenSuccess(_ callback: @escaping (Value) -> Void)

    Parameters

    callback

    The callback that is called with the successful result of the EventLoopFuture.

  • Adds an observer callback to this EventLoopFuture that is called when the EventLoopFuture has a failure result.

    An observer callback cannot return a value, meaning that this function cannot be chained from. If you are attempting to create a computation pipeline, consider recover or flatMapError. If you find yourself passing the results from this EventLoopFuture to a new EventLoopPromise in the body of this function, consider using cascade instead.

    Declaration

    Swift

    @inlinable
    public func whenFailure(_ callback: @escaping (Error) -> Void)

    Parameters

    callback

    The callback that is called with the failed result of the EventLoopFuture.

  • Adds an observer callback to this EventLoopFuture that is called when the EventLoopFuture has any result.

    Unlike its friends whenSuccess and whenFailure, whenComplete does not receive the result of the EventLoopFuture. This is because its primary purpose is to do the appropriate cleanup of any resources that needed to be kept open until the EventLoopFuture had resolved.

    Declaration

    Swift

    @inlinable
    public func whenComplete(_ callback: @escaping (Result<Value, Error>) -> Void)

    Parameters

    callback

    The callback that is called when the EventLoopFuture is fulfilled.

  • Return a new EventLoopFuture that succeeds when this and another provided EventLoopFuture both succeed. It then provides the pair of results. If either one fails, the combined EventLoopFuture will fail with the first error encountered.

    Declaration

    Swift

    @inlinable
    public func and<OtherValue>(_ other: EventLoopFuture<OtherValue>,
                                file: StaticString = #file,
                                line: UInt = #line) -> EventLoopFuture<(Value, OtherValue)>
  • Return a new EventLoopFuture that contains this and another value. This is just syntactic sugar for future.and(loop.makeSucceedFuture(value)).

    Declaration

    Swift

    @inlinable
    public func and<OtherValue>(value: OtherValue,
                                file: StaticString = #file,
                                line: UInt = #line) -> EventLoopFuture<(Value, OtherValue)>
  • Fulfills the given EventLoopPromise with the results from this EventLoopFuture.

    This is useful when allowing users to provide promises for you to fulfill, but when you are calling functions that return their own promises. They allow you to tidy up your computational pipelines.

    For example:

    doWork().flatMap {
        doMoreWork($0)
    }.flatMap {
        doYetMoreWork($0)
    }.flatMapError {
        maybeRecoverFromError($0)
    }.map {
        transformData($0)
    }.cascade(to: userPromise)
    

    Declaration

    Swift

    @inlinable
    public func cascade(to promise: EventLoopPromise<Value>?)

    Parameters

    to

    The EventLoopPromise to fulfill with the results of this future.

  • Fulfills the given EventLoopPromise only when this EventLoopFuture succeeds.

    If you are doing work that fulfills a type that doesn’t match the expected EventLoopPromise value, add an intermediate map.

    For example:

    let boolPromise = eventLoop.makePromise(of: Bool.self)
    doWorkReturningInt().map({ $0 >= 0 }).cascade(to: boolPromise)
    

    Declaration

    Swift

    @inlinable
    public func cascadeSuccess(to promise: EventLoopPromise<Value>?)

    Parameters

    to

    The EventLoopPromise to fulfill when a successful result is available.

  • Fails the given EventLoopPromise with the error from this EventLoopFuture if encountered.

    This is an alternative variant of cascade that allows you to potentially return early failures in error cases, while passing the user EventLoopPromise onwards.

    Declaration

    Swift

    @inlinable
    public func cascadeFailure<NewValue>(to promise: EventLoopPromise<NewValue>?)

    Parameters

    to

    The EventLoopPromise that should fail with the error of this EventLoopFuture.

  • Wait for the resolution of this EventLoopFuture by blocking the current thread until it resolves.

    If the EventLoopFuture resolves with a value, that value is returned from wait(). If the EventLoopFuture resolves with an error, that error will be thrown instead. wait() will block whatever thread it is called on, so it must not be called on event loop threads: it is primarily useful for testing, or for building interfaces between blocking and non-blocking code.

    Throws

    The error value of the EventLoopFuture if it errors.

    Declaration

    Swift

    public func wait(file: StaticString = #file, line: UInt = #line) throws -> Value

    Return Value

    The value of the EventLoopFuture when it completes.

  • Returns a new EventLoopFuture that fires only when this EventLoopFuture and all the provided futures complete. It then provides the result of folding the value of this EventLoopFuture with the values of all the provided futures.

    This function is suited when you have APIs that already know how to return EventLoopFutures.

    The returned EventLoopFuture will fail as soon as the a failure is encountered in any of the futures (or in this one). However, the failure will not occur until all preceding EventLoopFutures have completed. At the point the failure is encountered, all subsequent EventLoopFuture objects will no longer be waited for. This function therefore fails fast: once a failure is encountered, it will immediately fail the overall EventLoopFuture.

    Declaration

    Swift

    @inlinable
    public func fold<OtherValue>(_ futures: [EventLoopFuture<OtherValue>],
                                 with combiningFunction: @escaping (Value, OtherValue) -> EventLoopFuture<Value>) -> EventLoopFuture<Value>

    Parameters

    futures

    An array of EventLoopFuture<NewValue> to wait for.

    with

    A function that will be used to fold the values of two EventLoopFutures and return a new value wrapped in an EventLoopFuture.

    Return Value

    A new EventLoopFuture with the folded value whose callbacks run on self.eventLoop.

  • Returns a new EventLoopFuture that fires only when all the provided futures complete. The new EventLoopFuture contains the result of reducing the initialResult with the values of the [EventLoopFuture<NewValue>].

    This function makes copies of the result for each EventLoopFuture, for a version which avoids making copies, check out reduce<NewValue>(into:).

    The returned EventLoopFuture will fail as soon as a failure is encountered in any of the futures. However, the failure will not occur until all preceding EventLoopFutures have completed. At the point the failure is encountered, all subsequent EventLoopFuture objects will no longer be waited for. This function therefore fails fast: once a failure is encountered, it will immediately fail the overall EventLoopFuture.

    Declaration

    Swift

    public static func reduce<InputValue>(_ initialResult: Value,
                                          _ futures: [EventLoopFuture<InputValue>],
                                          on eventLoop: EventLoop,
                                          _ nextPartialResult: @escaping (Value, InputValue) -> Value) -> EventLoopFuture<Value>

    Parameters

    initialResult

    An initial result to begin the reduction.

    futures

    An array of EventLoopFuture to wait for.

    eventLoop

    The EventLoop on which the new EventLoopFuture callbacks will fire.

    nextPartialResult

    The bifunction used to produce partial results.

    Return Value

    A new EventLoopFuture with the reduced value.

  • Returns a new EventLoopFuture that fires only when all the provided futures complete. The new EventLoopFuture contains the result of combining the initialResult with the values of the [EventLoopFuture<NewValue>]. This function is analogous to the standard library’s reduce(into:), which does not make copies of the result type for each EventLoopFuture.

    The returned EventLoopFuture will fail as soon as a failure is encountered in any of the futures. However, the failure will not occur until all preceding EventLoopFutures have completed. At the point the failure is encountered, all subsequent EventLoopFuture objects will no longer be waited for. This function therefore fails fast: once a failure is encountered, it will immediately fail the overall EventLoopFuture.

    Declaration

    Swift

    public static func reduce<InputValue>(into initialResult: Value,
                                          _ futures: [EventLoopFuture<InputValue>],
                                          on eventLoop: EventLoop,
                                          _ updateAccumulatingResult: @escaping (inout Value, InputValue) -> Void) -> EventLoopFuture<Value>

    Parameters

    initialResult

    An initial result to begin the reduction.

    futures

    An array of EventLoopFuture to wait for.

    eventLoop

    The EventLoop on which the new EventLoopFuture callbacks will fire.

    updateAccumulatingResult

    The bifunction used to combine partialResults with new elements.

    Return Value

    A new EventLoopFuture with the combined value.

  • When the current EventLoopFuture<Value> is fulfilled, run the provided callback, which performs a synchronous computation and returns either a new value (of type NewValue) or an error depending on the Result returned by the closure.

    Operations performed in flatMapResult should not block, or they will block the entire event loop. flatMapResult is intended for use when you have a data-driven function that performs a simple data transformation that can potentially error.

    Declaration

    Swift

    @inlinable
    public func flatMapResult<NewValue, SomeError: Error>(file: StaticString = #file,
                                                          line: UInt = #line,
                                                          _ body: @escaping (Value) -> Result<NewValue, SomeError>) -> EventLoopFuture<NewValue>

    Parameters

    body

    Function that will receive the value of this EventLoopFuture and return a new value or error lifted into a new EventLoopFuture.

    Return Value

    A future that will receive the eventual value.

  • Returns a new EventLoopFuture that succeeds only if all of the provided futures succeed.

    This method acts as a successful completion notifier - values fulfilled by each future are discarded.

    The returned EventLoopFuture fails as soon as any of the provided futures fail.

    If it is desired to always succeed, regardless of failures, use andAllComplete instead.

    Declaration

    Swift

    public static func andAllSucceed(_ futures: [EventLoopFuture<Value>], on eventLoop: EventLoop) -> EventLoopFuture<Void>

    Parameters

    futures

    An array of homogenous EventLoopFuturess to wait for.

    on

    The EventLoop on which the new EventLoopFuture callbacks will execute on.

    Return Value

    A new EventLoopFuture that waits for the other futures to succeed.

  • Returns a new EventLoopFuture that succeeds only if all of the provided futures succeed. The new EventLoopFuture will contain all of the values fulfilled by the futures.

    The returned EventLoopFuture will fail as soon as any of the futures fails.

    Declaration

    Swift

    public static func whenAllSucceed(_ futures: [EventLoopFuture<Value>], on eventLoop: EventLoop) -> EventLoopFuture<[Value]>

    Parameters

    futures

    An array of homogenous EventLoopFutures to wait on for fulfilled values.

    on

    The EventLoop on which the new EventLoopFuture callbacks will fire.

    Return Value

    A new EventLoopFuture with all of the values fulfilled by the provided futures.

  • Returns a new EventLoopFuture that succeeds when all of the provided EventLoopFutures complete.

    The returned EventLoopFuture always succeeds, acting as a completion notification. Values fulfilled by each future are discarded.

    If the results are needed, use whenAllComplete instead.

    Declaration

    Swift

    public static func andAllComplete(_ futures: [EventLoopFuture<Value>], on eventLoop: EventLoop) -> EventLoopFuture<Void>

    Parameters

    futures

    An array of homogenous EventLoopFutures to wait for.

    on

    The EventLoop on which the new EventLoopFuture callbacks will execute on.

    Return Value

    A new EventLoopFuture that succeeds after all futures complete.

  • Returns a new EventLoopFuture that succeeds when all of the provided EventLoopFutures complete. The new EventLoopFuture will contain an array of results, maintaining ordering for each of the EventLoopFutures.

    The returned EventLoopFuture always succeeds, regardless of any failures from the waiting futures.

    If it is desired to flatten them into a single EventLoopFuture that fails on the first EventLoopFuture failure, use one of the reduce methods instead.

    Declaration

    Swift

    public static func whenAllComplete(_ futures: [EventLoopFuture<Value>],
                                       on eventLoop: EventLoop) -> EventLoopFuture<[Result<Value, Error>]>

    Parameters

    futures

    An array of homogenous EventLoopFutures to gather results from.

    on

    The EventLoop on which the new EventLoopFuture callbacks will fire.

    Return Value

    A new EventLoopFuture with all the results of the provided futures.

  • Returns an EventLoopFuture that fires when this future completes, but executes its callbacks on the target event loop instead of the original one.

    It is common to want to hop event loops when you arrange some work: for example, you’re closing one channel from another, and want to hop back when the close completes. This method lets you spell that requirement succinctly. It also contains an optimisation for the case when the loop you’re hopping from is the same as the one you’re hopping to, allowing you to avoid doing allocations in that case.

    Declaration

    Swift

    @inlinable
    public func hop(to target: EventLoop) -> EventLoopFuture<Value>

    Parameters

    to

    The EventLoop that the returned EventLoopFuture will run on.

    Return Value

    An EventLoopFuture whose callbacks run on target instead of the original loop.

  • Adds an observer callback to this EventLoopFuture that is called when the EventLoopFuture has any result.

    Declaration

    Swift

    public func always(_ callback: @escaping (Result<Value, Error>) -> Void) -> EventLoopFuture<Value>

    Parameters

    callback

    the callback that is called when the EventLoopFuture is fulfilled.

    Return Value

    the current EventLoopFuture