GCD pattern for chaining async operations while piping the results

Coming from JavaScript world using async javascript promises, and I believe the same is done using GCD async queues in Swift.

Can you point me to an example where 2 to 3 async functions are specified in a queue, with one async operation feeding the result to the second, and second feeding the result to the third (commonly known as piping the results) and then finally a result and error handler.

  • Obj-C: Why is my NSTextField subclass having an effect on objects I haven't assigned to it?
  • Asynchronously set images in tableview
  • How to embed an executable in my project
  • Available Notifications For NSDistributedNotificationCenter
  • How to embed ParseKit as a private framework in a Mac App bundle
  • How can I get unmanaged object from Realm query in Swift?
  • All functions restrict to a single argument by design.

    If any error let’s say during function 2, function 3 is skipped and error is passed straight down to the error handler.

    Looking for a native solution instead of any 3rd party Promise or Monad library.

    Appreciate swift 3.0 code.

    Edit. Also learned from the example that steps are more like a manual linear path in GCD where developer is the one feeding result to the next and checking error each time, Any Functional Programming possible using Function Compositions?

    I want to avoid Pyramid of Doom and looking for a linear async programming.

    2 Solutions Collect From Internet About “GCD pattern for chaining async operations while piping the results”

    I’ve reached out to Apple with a support request and they referred me to this WWDC15 video Advance NSOperations.

    I didn’t get to see how to pipe results from one sub operation to the other, now based on this video if someone can write some comprehensive example code where one routine after executing passing results to the next, I’d accept the answer.

    There is no support for that in the standard library and there probably won’t be for a while. There are great third-party libraries, but if you don’t want those, you could define a minimum yourself (I’m using Swift 3 here):

    enum Result<R> {
        case Success(R)
        case Failure(ErrorProtocol)
    }
    
    typealias Async<A, B> = (a: A, handler: (Result<B>) -> Void) -> Void
    
    infix operator • {
        associativity right
        precedence 190
    }
    
    func •<A, B, C>(f: Async<A, B>, g: Async<B, C>) -> Async<A, C> {
        return { a, handler in
            f(a: a, handler: { result in
                switch result {
                case .Success(let b): g(a: b, handler: handler)
                case .Failure(let e): handler(.Failure(e))
                }
            })
        }
    }
    

    Usage example:

    func f(n: Int, h: (Result<String>) -> ()) {
        h(.Success(n.description))
    }
    
    func g(s: String, h: (Result<Int>) -> ()) {
        h(.Success(s.characters.count))
    }
    
    let chained = f • g
    
    chained(a: 10) { result in
        switch result {
        case .Success(let r): print("Success: \(r)")
        case .Failure(let e): print("Error: \(e)")
        }
    }
    

    Advanced usage example:

    enum Error : ErrorProtocol {
        case NoResult
        case StringDecoding
    }
    
    extension URLSession {
        func getData(with url: URL, completionHandler: (Result<(Data, URLResponse)>) -> Void) {
            let task = dataTask(with: url) { (data, response, error) in
                if let error = error {
                    completionHandler(.Failure(error))
                } else if let data = data, response = response {
                    completionHandler(.Success((data, response)))
                } else {
                    completionHandler(.Failure(Error.NoResult))
                }
            }
    
            task.resume()
        }
    }
    
    func decode(d: (Data, URLResponse), handler: (Result<String>) -> Void) {
        DispatchQueue(label: "async").async{
            if let string = String(data: d.0, encoding: .utf8) {
                handler(.Success(string))
            } else {
                handler(.Failure(Error.StringDecoding))
            }
        }
    }
    
    let getString = URLSession.shared().getData • decode
    
    
    getString(a: URL(string: "https://www.reddit.com")!) { result in
        switch result {
        case .Success(let string): print(string)
        case .Failure(let e): print(e)
        }
    }