Skip to content

Instantly share code, notes, and snippets.

@kingiol
Created February 11, 2019 06:09
Show Gist options
  • Save kingiol/e54b3f9f89a9c4006a993845d859c68a to your computer and use it in GitHub Desktop.
Save kingiol/e54b3f9f89a9c4006a993845d859c68a to your computer and use it in GitHub Desktop.
Task - single task, group tasks, sequence tasks
struct Task {
enum Result {
case success
case failure(Error)
}
struct Controller {
fileprivate let queue: DispatchQueue
fileprivate let handler: (Result) -> Void
func finish() {
handler(.success)
}
func fail(with error: Error) {
handler(.failure(error))
}
}
typealias Closure = (Controller) -> Void
private let closure: Closure
init(closure: @escaping Closure) {
self.closure = closure
}
func perform(on queue: DispatchQueue = .global(),
then handler: @escaping (Result) -> Void) {
queue.async { [weak self] in
let controller = Controller(queue: queue, handler: handler)
self?.closure(controller)
}
}
}
// MARK: - Single Task
extension Task {
static func demoUploadingTask() -> Task {
return Task { (controller) in
// run uploading data to server
// uploader.uploading(data) { error in
// if let error = error {
// controller.fail(with: error)
// } else {
controller.finish()
// }
}
}
}
// MARK: - Group Tasks
extension Task {
static func group(_ tasks: [Task]) -> Task {
return Task { controller in
let group = DispatchGroup()
var anyError: Error?
for task in tasks {
group.enter()
// It's important to make the sub-tasks execute on the same DispatchQueue as the group,
// Since we might cause unexpected threading issue otherwise.
task.perform(on: controller.queue, then: { (result) in
switch result {
case .success:
break
case .failure(let error):
anyError = error
}
group.leave()
})
}
group.notify(queue: controller.queue) {
if let error = anyError {
controller.fail(with: error)
} else {
controller.finish()
}
}
}
}
}
// MARK: - Sequence Tasks
extension Task {
static func sequence(_ tasks: [Task]) -> Task {
var index = 0
func performNext(using controller: Controller) {
guard index < tasks.count else {
// We've reached the end of our array of tasks, time to finish the sequence
controller.finish()
return
}
let task = tasks[index]
index += 1
task.perform(on: controller.queue) { result in
switch result {
case .success:
performNext(using: controller)
case .failure(let error):
// As soon as an error was occurred, we'll faile the entire sequence.
controller.fail(with: error)
}
}
}
return Task(closure: performNext)
}
}
// Demo single task
let uploadingTask = Task.demoUploadingTask()
uploadingTask.perform { (result) in
// handle uploading result
}
// Demo group tasks
let uploadintTask1 = Task.demoUploadingTask()
let uploadintTask2 = Task.demoUploadingTask()
let groupTask = Task.group([uploadintTask1, uploadintTask2])
groupTask.perform { (result) in
// handle group task result
}
// Demo sequence tasks
let uploadingTask3 = Task.demoUploadingTask()
let uploadingTask4 = Task.demoUploadingTask()
let publishPostTask = Task.demoUploadingTask()
let sequenceTasks = Task.sequence([Task.group([uploadingTask3, uploadingTask4]), publishPostTask])
sequenceTasks.perform { (result) in
// handle sequence task result
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment