Skip to content

Instantly share code, notes, and snippets.

@Gujci
Last active May 16, 2023 08:52
Show Gist options
  • Save Gujci/5628f873ffe6365ead9da3f689440a7c to your computer and use it in GitHub Desktop.
Save Gujci/5628f873ffe6365ead9da3f689440a7c to your computer and use it in GitHub Desktop.
swift implementation of the famous async-waterfall
public class Async {
/// Throw version implementation of waterfall
///
/// - Parameters:
/// - initialValue: initial passed value of the chain
/// - chain: chain of closures to process
/// - end: last callback. Called after the chain finished or in an error occures
/// - Throws: Any error that one of the chain elements throwed
///
/// - Note: Each callback should be called only once during executon
class func waterfall(_ initialValue: Any? = nil,_ chain:[(@escaping (Any?) throws -> (), Any?) throws -> ()],end: @escaping (Any?) -> () ) throws {
guard let function = chain.first else {
end(initialValue)
return
}
try function({ (newResult: Any?) in try waterfall(newResult, Array(chain.dropFirst()), end: end) } , initialValue)
}
/// Error first callback
/// The problem with throwing are async, non-rethowing functions 😨 .. so we need both
///
/// - Parameters:
/// - initialValue: initial passed value of the chain
/// - chain: chain of closures to process
/// - end: last callback. Called after the chain finished or in an error occures
///
/// - Note: Each callback should be called only once during executon
class func waterfall(_ initialValue: Any? = nil,_ chain:[(@escaping (Error?, Any?) -> (), Any?) -> ()],end: @escaping (Error?, Any?) -> () ) {
guard let function = chain.first else {
end(nil, initialValue)
return
}
function({ (err: Error?, newResult: Any?) in
if let err = err {
end(err, nil)
return
}
waterfall(newResult, Array(chain.dropFirst()), end: end)
}, initialValue)
}
/// Combined
/// Each chain function can throw, but the waterfall will get the error in the end callback, best use if all async calls are rethrowing, and the result and error has to be handled at the same place
///
/// - Parameters:
/// - initialValue: initial passed value of the chain
/// - chain: chain of closures to process
/// - end: last callback. Called after the chain finished or in an error occures
///
/// - Note: Each callback should be called only once during executon
class func waterfall(_ initialValue: Any? = nil,_ chain:[(@escaping (Any?) -> (), Any?) throws -> ()],end: @escaping (Error?, Any?) -> () ) {
guard let function = chain.first else {
end(nil, initialValue)
return
}
do {
try function({ (newResult: Any?) in waterfall(newResult, Array(chain.dropFirst()), end: end) }, initialValue)
}
catch let error {
end(error, nil)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment