<div dir="ltr">Hi All,<div><br></div><div>Really glad that concurrency is on the table for Swift 5.<br><div><br></div><div>I am not sure if async/await are worth adding, as is, to Swift because it is just as easy to do with a library function - in the spirit of Swift 5, see `Future` library code below that you can play with and run.<div><br></div><div>If a `Future` class was added and the compiler translated &#39;completion-handler&#39; code to &#39;future&#39; code then the running example given in the whitepaper would become (also see the example in the code below):</div><div><br></div><blockquote style="margin:0px 0px 0px 40px;border:none;padding:0px"><div>func loadWebResource(_ path: String) -&gt; Future&lt;Resource&gt; { ... }</div><div>func decodeImage(_ dataResource: Resource, _ imageResource: Resource) -&gt; Future&lt;Image&gt; { ... }</div><div>func dewarpAndCleanupImage(_ image: Image) -&gt; Future&lt;Image&gt; { ... }</div><div><br></div><div>func processImageData1() -&gt; Future&lt;Image&gt; {</div><div>    let dataResource  = loadWebResource(&quot;dataprofile.txt&quot;) // dataResource and imageResource run in parallel.</div><div>    let imageResource = loadWebResource(&quot;imagedata.dat&quot;)</div><div>    let imageTmp      = decodeImage(dataResource.get ?? Resource(path: &quot;Default data resource or prompt user&quot;), imageResource.get ?? Resource(path: &quot;Default image resource or prompt user&quot;))</div><div>    let imageResult   =  dewarpAndCleanupImage(imageTmp.get ?? Image(dataPath: &quot;Default image or prompt user&quot;, imagePath: &quot;Default image or prompt user&quot;))</div><div>    return imageResult</div><div>}</div></blockquote><div><div><div class="gmail_signature"> </div><div class="gmail_signature">Which I would argue is actually better than the proposed async/await code because:</div><div class="gmail_signature"><ol><li>The code is naturally parallel, in example dataResource and imageResource are calculated in parallel. <br></li><li>The code handles errors and deadlocks by providing a default value using `??`.</li><li>The code can be deadlock free or can help find deadlocks, see code below, due to having a timeout and a means of cancelling.</li><li>The programmer who writes the creator of the `Future` controls which queue the future executes on, I would contend that this is the best choice since the programmer knows what limitations are in the code and can change queue if the code changes in later versions. This is the same argument as encapsulation, which gives more control to the writer of a struct/class and less control to the user of the struct/class.</li></ol></div><div class="gmail_signature">In summary, I don&#39;t think async/await carries its weight (pun intended), as it stands, compared to a library.</div><div class="gmail_signature"><br></div><div class="gmail_signature">But definitely for more Swift concurrency,</div><div class="gmail_signature"><br></div><div class="gmail_signature"> -- Howard.<br></div></div><div class="gmail_signature"><br></div><div class="gmail_signature">PS A memory model and atomic that also guaranteed volatile would really help with parallel programming!</div><div class="gmail_signature"><br></div><div class="gmail_signature">===========================================================</div><div class="gmail_signature"><br></div></div></div></div><blockquote style="margin:0 0 0 40px;border:none;padding:0px"><div><div><div>import Foundation</div></div></div><div><div><div><br></div></div></div><div><div><div>/// - note:</div></div></div><div><div><div>///   - Written in GCD but execution service would be abstracted for a &#39;real&#39; version of this proposed `Future`.</div></div></div><div><div><div>///   - It might be necessary to write an atomic class/struct and use it for _status and isCancelled in CalculatingFuture; see comments after property declarations.</div></div></div><div><div><div>///   - If _status and isCancelled in CalculatingFuture where atomic then future would be thread safe.</div></div></div><div><div><div><br></div></div></div><div><div><div>/// All possible states for a `Future`; a future is in exactly one of these.</div></div></div><div><div><div>enum FutureStatus&lt;T&gt; {</div></div></div><div><div><div>    /// Currently running or waiting to run; has not completed, was not cancelled, has not timed out, and has not thrown.</div></div></div><div><div><div>    case running</div></div></div><div><div><div><br></div></div></div><div><div><div>    /// Ran to completion; was not cancelled, did not timeout, and did not throw, no longer running.</div></div></div><div><div><div>    case completed(result: T)</div></div></div><div><div><div><br></div></div></div><div><div><div>    /// Was cancelled, timed out, or calculation threw an exception; no longer running.</div></div></div><div><div><div>    case threw(error: Error)</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>/// An error that signals the future was cancelled.</div></div></div><div><div><div>enum CancelFuture: Error {</div></div></div><div><div><div>    /// Should be thrown by a future&#39;s calculation when requested to do so via its `isCancelled` argument (which arises if the future is cancelled or if the future times out).</div></div></div><div><div><div>    case cancelled</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>/// Base class for futures; acts like a future that was cancelled, i.e. no result and threw `CancelFuture.cancelled`.</div></div></div><div><div><div>/// - note:</div></div></div><div><div><div>///   - You would normally program to `Future`, not one of its derived classes, i.e. arguments, return types, properties, etc. typed as `Future`.</div></div></div><div><div><div>///   - Futures are **not** thread safe; i.e. they cannot be shared between threads though their results can and they themselves can be inside any single thread.</div></div></div><div><div><div>///   - This class is useful in its own right; not just a base class, but as a future that is known to be cancelled.</div></div></div><div><div><div>class Future&lt;T&gt; {</div></div></div><div><div><div>    /// The current state of execution of the future.</div></div></div><div><div><div>    /// - note:</div></div></div><div><div><div>    ///   - The status is updated when the future&#39;s calculation finishes; therefore there will be a lag between a cancellation or a timeout and status reflecting this.</div></div></div><div><div><div>    ///   - This status lag is due to the underlying thread system provided by the operating system that typically does not allow a running thread to be terminated.</div></div></div><div><div><div>    ///   - Because status can lag cancel and timeout; prefer get over status, for obtaining the result of a future and if detailed reasons for a failure are not required.</div></div></div><div><div><div>    ///   - Status however offers detailed information if a thread terminates by throwing (including cancellation and time out) and is therefore very useful for debugging.</div></div></div><div><div><div>    /// - note: In the case of this base class, always cancelled; returns `.threw(error: CancelFuture.cancelled)`.</div></div></div><div><div><div>    var status: FutureStatus&lt;T&gt; {</div></div></div><div><div><div>        return .threw(error: CancelFuture.cancelled)</div></div></div><div><div><div>    }</div></div></div><div><div><div><br></div></div></div><div><div><div>    /// Wait until the value of the future is calculated and return it; if future timed out, if future was cancelled, or if calculation threw, then return nil.</div></div></div><div><div><div>    /// The intended use of this property is to chain with the nil coalescing operator, `??`, to provide a default, a retry, or an error message in the case of failure.</div></div></div><div><div><div>    /// - note:</div></div></div><div><div><div>    ///   - Timeout is only checked when `get` is called.</div></div></div><div><div><div>    ///   - If a future is cancelled or times out then get will subsequently return nil; however it might take some time before status reflects this calculation because status is only updated when the calculation stops.</div></div></div><div><div><div>    /// - note: In the case of this base class, always return nil.</div></div></div><div><div><div>    var get: T? {</div></div></div><div><div><div>        return nil</div></div></div><div><div><div>    }</div></div></div><div><div><div><br></div></div></div><div><div><div>    /// Cancel the calculation of the future; if it has not already completed.</div></div></div><div><div><div>    /// - note:</div></div></div><div><div><div>    ///   - Cancellation causes `CancelFuture.cancelled` to be thrown and hence the future&#39;s status changes to `threw`.</div></div></div><div><div><div>    ///   - Cancellation will not be instantaneous and therefore the future&#39;s status will not update immediately; it updates when the calculation terminates (either by returning a value or via a throw).</div></div></div><div><div><div>    ///   - If a future timeouts, it cancels its calculation.</div></div></div><div><div><div>    ///   - If the future&#39;s calculation respects its `isCancelled` argument then a timeout will break a deadlock.</div></div></div><div><div><div>    ///   - If a future is cancelled by either cancel or a timeout, subsequent calls to `get` will return nil; even if the calculation is still running and hence status has not updated.</div></div></div><div><div><div>    /// - note: In the case of this base class, cancel does nothing since this future is always cancelled.</div></div></div><div><div><div>    func cancel() {}</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>/// A future that calculates its value on the given queue and has the given timeout to bound wait time when `get` is called.</div></div></div><div><div><div>final class CalculatingFuture&lt;T&gt;: Future&lt;T&gt; {</div></div></div><div><div><div>    private var _status = FutureStatus&lt;T&gt;.running // Really like to mark this volatile and atomic (it is written in background thread and read in foreground)!</div></div></div><div><div><div><br></div></div></div><div><div><div>    override var status: FutureStatus&lt;T&gt; {</div></div></div><div><div><div>        return _status</div></div></div><div><div><div>    }</div></div></div><div><div><div><br></div></div></div><div><div><div>    private let group = DispatchGroup()</div></div></div><div><div><div><br></div></div></div><div><div><div>    private let timeoutTime: DispatchTime</div></div></div><div><div><div><br></div></div></div><div><div><div>    private var isCancelled = false // Really like to mark this volatile (it is a bool so presumably atomic, but it is set in forground thread and read in background)!</div></div></div><div><div><div><br></div></div></div><div><div><div>    /// - note: The default queue is the global queue with default quality of service.</div></div></div><div><div><div>    /// - note:</div></div></div><div><div><div>    ///   Regarding the `timeout` argument:</div></div></div><div><div><div>    ///   - Timeout starts from when the future is created, not when `get` is called.</div></div></div><div><div><div>    ///   - The time used for a timeout is processor time; i.e. it excludes time when the computer is in sleep mode.</div></div></div><div><div><div>    ///   - The default timeout is 2 seconds.</div></div></div><div><div><div>    ///   - If the calculation times out then the calculation is cancelled.</div></div></div><div><div><div>    ///   - The timeout is only checked when `get` is called; i.e. the calculation will continue for longer than timeout, potentially indefinitely, if `get` is not called.</div></div></div><div><div><div>    ///   - Also see warning below.</div></div></div><div><div><div>    /// - warning:</div></div></div><div><div><div>    ///   Be **very** careful about setting long timeouts; if a deadlock occurs it is diagnosed by a timeout occurring!</div></div></div><div><div><div>    ///   If the calculating method respects its `isCancelled` argument a timeout will also break a deadlock.</div></div></div><div><div><div>    init(queue: DispatchQueue = DispatchQueue.global(), timeout: DispatchTimeInterval = DispatchTimeInterval.seconds(2), calculation: @escaping (_ isCancelled: () -&gt; Bool) throws -&gt; T) {</div></div></div><div><div><div>        self.timeoutTime = DispatchTime.now() + timeout</div></div></div><div><div><div>        super.init() // Have to complete initialization before result can be calculated.</div></div></div><div><div><div>        queue.async { [weak self] in</div></div></div><div><div><div>            guard let strongSelf = self else { // Future is no longer required; it no longer exists.</div></div></div><div><div><div>                return</div></div></div><div><div><div>            }</div></div></div><div><div><div>            strongSelf.group.enter()</div></div></div><div><div><div>            do {</div></div></div><div><div><div>                guard !strongSelf.isCancelled else { // Future was cancelled before execution began.</div></div></div><div><div><div>                    throw CancelFuture.cancelled</div></div></div><div><div><div>                }</div></div></div><div><div><div>                let result = try calculation {</div></div></div><div><div><div>                    strongSelf.isCancelled</div></div></div><div><div><div>                }</div></div></div><div><div><div>                guard !strongSelf.isCancelled else { // Future was cancelled during execution.</div></div></div><div><div><div>                    throw CancelFuture.cancelled</div></div></div><div><div><div>                }</div></div></div><div><div><div>                strongSelf._status = .completed(result: result)</div></div></div><div><div><div>            } catch {</div></div></div><div><div><div>                strongSelf._status = .threw(error: error)</div></div></div><div><div><div>            }</div></div></div><div><div><div>            strongSelf.group.leave()</div></div></div><div><div><div>        }</div></div></div><div><div><div>    }</div></div></div><div><div><div><br></div></div></div><div><div><div>    override var get: T? {</div></div></div><div><div><div>        guard !isCancelled else { // Catch waiting for a cancel to actually happen.</div></div></div><div><div><div>            return nil</div></div></div><div><div><div>        }</div></div></div><div><div><div>        while true { // Loop until not running, so that after a successful wait the result can be obtained.</div></div></div><div><div><div>            switch _status {</div></div></div><div><div><div>            case .running:</div></div></div><div><div><div>                switch group.wait(timeout: timeoutTime) { // Wait for calculation completion.</div></div></div><div><div><div>                case .success:</div></div></div><div><div><div>                    break // Loop round and test status again to extract result</div></div></div><div><div><div>                case .timedOut:</div></div></div><div><div><div>                    isCancelled = true</div></div></div><div><div><div>                    return nil</div></div></div><div><div><div>                }</div></div></div><div><div><div>            case .completed(let result):</div></div></div><div><div><div>                return result</div></div></div><div><div><div>            case .threw(_):</div></div></div><div><div><div>                return nil</div></div></div><div><div><div>            }</div></div></div><div><div><div>        }</div></div></div><div><div><div>    }</div></div></div><div><div><div><br></div></div></div><div><div><div>    override func cancel() {</div></div></div><div><div><div>        switch _status {</div></div></div><div><div><div>        case .running:</div></div></div><div><div><div>            isCancelled = true</div></div></div><div><div><div>        case .completed(_):</div></div></div><div><div><div>            return // Cannot cancel a completed future.</div></div></div><div><div><div>        case .threw(_):</div></div></div><div><div><div>            return // Cannot cancel a future that has timed out, been cancelled, or thrown.</div></div></div><div><div><div>        }</div></div></div><div><div><div>    }</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>/// A future that doesn&#39;t need calculating, because the result is already known.</div></div></div><div><div><div>final class KnownFuture&lt;T&gt;: Future&lt;T&gt; {</div></div></div><div><div><div>    private let result: T</div></div></div><div><div><div>    override var status: FutureStatus&lt;T&gt; {</div></div></div><div><div><div>        return .completed(result: result)</div></div></div><div><div><div>    }</div></div></div><div><div><div><br></div></div></div><div><div><div>    init(_ result: T) {</div></div></div><div><div><div>        self.result = result</div></div></div><div><div><div>    }</div></div></div><div><div><div><br></div></div></div><div><div><div>    override var get: T? {</div></div></div><div><div><div>        return result</div></div></div><div><div><div>    }</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div><br></div></div></div><div><div><div>/// A future that doesn&#39;t need calculating, because it is known to fail.</div></div></div><div><div><div>final class FailedFuture&lt;T&gt;: Future&lt;T&gt; {</div></div></div><div><div><div>    private let _status: FutureStatus&lt;T&gt;</div></div></div><div><div><div><br></div></div></div><div><div><div>    override var status: FutureStatus&lt;T&gt; {</div></div></div><div><div><div>        return _status</div></div></div><div><div><div>    }</div></div></div><div><div><div><br></div></div></div><div><div><div>    init(_ error: Error) {</div></div></div><div><div><div>        _status = .threw(error: error)</div></div></div><div><div><div>    }</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div><br></div></div></div><div><div><div>// Example from <a href="https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619">https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619</a></div></div></div><div><div><div>// Various minor changes necessary to get example to compile, plus missing types and functions added.</div></div></div><div><div><div><br></div></div></div><div><div><div>// Needed to make the example run.</div></div></div><div><br></div><div><div><div>struct Image {</div></div></div><div><div><div>    let dataPath: String</div></div></div><div><div><div>    let imagePath: String</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>struct Resource {</div></div></div><div><div><div>    let path: String</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>func loadWebResource(_ path: String, completion: (_ dataResource: Resource?, _ error: Error?) -&gt; Void) {</div></div></div><div><div><div>    completion(Resource(path: path), nil)</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>func decodeImage(_ dataResource: Resource, _ imageResource: Resource, completion: (_ imageResult: Image?, _ error: Error?) -&gt; Void) {</div></div></div><div><div><div>    completion(Image(dataPath: dataResource.path, imagePath: imageResource.path), nil)</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div><br></div></div></div><div><div><div>func dewarpAndCleanupImage(_ image: Image, completion: (_ imageResult: Image?, _ error: Error?) -&gt; Void) {</div></div></div><div><div><div>    completion(image, nil)</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>// The example in &#39;completion-handler&#39; form (with typo fixes).</div></div></div><div><div><div>// Doesn&#39;t actually run on a background thread; for simplicity, unlike `Future` form.</div></div></div><div><div><div><br></div></div></div><div><div><div>func processImageData2(completionBlock: @escaping (_ result: Image?, _ error: Error?) -&gt; Void) {</div></div></div><div><div><div>    loadWebResource(&quot;dataprofile.txt&quot;) { dataResource, error in</div></div></div><div><div><div>        guard let dataResource = dataResource else {</div></div></div><div><div><div>            completionBlock(nil, error)</div></div></div><div><div><div>            return</div></div></div><div><div><div>        }</div></div></div><div><div><div>        loadWebResource(&quot;imagedata.dat&quot;) { imageResource, error in</div></div></div><div><div><div>            guard let imageResource = imageResource else {</div></div></div><div><div><div>                completionBlock(nil, error)</div></div></div><div><div><div>                return</div></div></div><div><div><div>            }</div></div></div><div><div><div>            decodeImage(dataResource, imageResource) { imageTmp, error in</div></div></div><div><div><div>                guard let imageTmp = imageTmp else {</div></div></div><div><div><div>                    completionBlock(nil, error)</div></div></div><div><div><div>                    return</div></div></div><div><div><div>                }</div></div></div><div><div><div>                dewarpAndCleanupImage(imageTmp) { imageResult, error in</div></div></div><div><div><div>                    guard let imageResult = imageResult else {</div></div></div><div><div><div>                        completionBlock(nil, error)</div></div></div><div><div><div>                        return</div></div></div><div><div><div>                    }</div></div></div><div><div><div>                    completionBlock(imageResult, nil)</div></div></div><div><div><div>                }</div></div></div><div><div><div>            }</div></div></div><div><div><div>        }</div></div></div><div><div><div>    }</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>func errorPrint(_ error: Error?) {</div></div></div><div><div><div>    guard let error = error else {</div></div></div><div><div><div>        return</div></div></div><div><div><div>    }</div></div></div><div><div><div>    print(error)</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>func imagePrint(_ image: Image?) {</div></div></div><div><div><div>    guard let image = image else {</div></div></div><div><div><div>        return</div></div></div><div><div><div>    }</div></div></div><div><div><div>    print(image)</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div><br></div></div></div><div><div><div>// Not actually running on a queue so don&#39;t need to wait for completion!</div></div></div><div><div><div>print(&quot;Completion-handler form&quot;)</div></div></div><div><div><div>processImageData2 { image, error in</div></div></div><div><div><div>    imagePrint(image)</div></div></div><div><div><div>    errorPrint(error)</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>// Compiler generated, `Future` versions of functions with continuation handlers.</div></div></div><div><div><div><br></div></div></div><div><div><div>enum ResultOrError&lt;T&gt; {</div></div></div><div><div><div>    case result(T)</div></div></div><div><div><div>    case error(Error)</div></div></div><div><div><div>    case notInitializedYet</div></div></div><div><div><div>    func value() throws -&gt; T {</div></div></div><div><div><div>        switch self {</div></div></div><div><div><div>        case .result(let result):</div></div></div><div><div><div>            return result</div></div></div><div><div><div>        case .error(let error):</div></div></div><div><div><div>            throw error</div></div></div><div><div><div>        case .notInitializedYet:</div></div></div><div><div><div>            fatalError(&quot;Bug in automatically generated function.&quot;)</div></div></div><div><div><div>        }</div></div></div><div><div><div>    }</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>func loadWebResource(_ path: String) -&gt; Future&lt;Resource&gt; {</div></div></div><div><div><div>    return CalculatingFuture { _ in</div></div></div><div><div><div>        var resultOrError = ResultOrError&lt;Resource&gt;.notInitializedYet</div></div></div><div><div><div>        loadWebResource(path) { dataResource, error in</div></div></div><div><div><div>            if error == nil {</div></div></div><div><div><div>                resultOrError = .result(dataResource!)</div></div></div><div><div><div>            } else {</div></div></div><div><div><div>                resultOrError = .error(error!)</div></div></div><div><div><div>            }</div></div></div><div><div><div>        }</div></div></div><div><div><div>        return try resultOrError.value()</div></div></div><div><div><div>    }</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>func decodeImage(_ dataResource: Resource, _ imageResource: Resource) -&gt; Future&lt;Image&gt; {</div></div></div><div><div><div>    return CalculatingFuture { _ in</div></div></div><div><div><div>        var resultOrError = ResultOrError&lt;Image&gt;.notInitializedYet</div></div></div><div><div><div>        decodeImage(dataResource, imageResource) { image, error in</div></div></div><div><div><div>            if error == nil {</div></div></div><div><div><div>                resultOrError = .result(image!)</div></div></div><div><div><div>            } else {</div></div></div><div><div><div>                resultOrError = .error(error!)</div></div></div><div><div><div>            }</div></div></div><div><div><div>        }</div></div></div><div><div><div>        return try resultOrError.value()</div></div></div><div><div><div>    }</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>func dewarpAndCleanupImage(_ image: Image) -&gt; Future&lt;Image&gt; {</div></div></div><div><div><div>    return CalculatingFuture { _ in</div></div></div><div><div><div>        var resultOrError = ResultOrError&lt;Image&gt;.notInitializedYet</div></div></div><div><div><div>        dewarpAndCleanupImage(image) { imageResult, error in</div></div></div><div><div><div>            if error == nil {</div></div></div><div><div><div>                resultOrError = .result(imageResult!)</div></div></div><div><div><div>            } else {</div></div></div><div><div><div>                resultOrError = .error(error!)</div></div></div><div><div><div>            }</div></div></div><div><div><div>        }</div></div></div><div><div><div>        return try resultOrError.value()</div></div></div><div><div><div>    }</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>// The example in &#39;future&#39; form (runs on a background queue).</div></div></div><div><div><div>func processImageData1() -&gt; Future&lt;Image&gt; {</div></div></div><div><div><div>    let dataResource  = loadWebResource(&quot;dataprofile.txt&quot;) // dataResource and imageResource run in parallel.</div></div></div><div><div><div>    let imageResource = loadWebResource(&quot;imagedata.dat&quot;)</div></div></div><div><div><div>    let imageTmp      = decodeImage(dataResource.get ?? Resource(path: &quot;Default data resource or prompt user&quot;), imageResource.get ?? Resource(path: &quot;Default image resource or prompt user&quot;))</div></div></div><div><div><div>    let imageResult   =  dewarpAndCleanupImage(imageTmp.get ?? Image(dataPath: &quot;Default image or prompt user&quot;, imagePath: &quot;Default image or prompt user&quot;))</div></div></div><div><div><div>    return imageResult</div></div></div><div><div><div>}</div></div></div><div><div><div><br></div></div></div><div><div><div>print(&quot;\nFuture form&quot;)</div></div></div><div><div><div>let processedImage = processImageData1()</div></div></div><div><div><div>let _ = processedImage.get // Wait for calculation to complete or timeout.</div></div></div><div><div><div>print(processedImage.status) // Status is useful for testing!</div></div></div></blockquote><div><div><div><br>
</div></div></div></div>