Is there a specific way to append DispatchWorkItem

2019-08-22 03:00发布

问题:

I have several Dispatch work items to execute on a queue i don't want to redeclare the codes, i want to pass them to an array or list of DispatchWorkItems and then inject it to a dispatch queue is there any way to achieve this ?

    func executeDispatchWorkItem(url: String, completion  : @escaping  (Result<String,Error>)-> Void,beganHandler  : @escaping  (String)-> Void){

do {
    beganHandler("\(url) Began to execute ")
    let content = try String(contentsOf:URL(string: url)!)
    completion(.success(content))
}
catch let error {
        completion(.failure(error))
}
sleep(1)

 }

   var serialQueue = DispatchQueue(label: "A queue")

  serialQueue.async {
executeDispatchWorkItem(url: "https://www.google.com/",completion: 
{data in

    switch data {
    case .success(let data):
        print("URL : \(data) completed  with \(String(describing: data))")
    case .failure(let error ):
        print("URL : \(error.localizedDescription) failed  with \(error.localizedDescription)")
    }

}, beganHandler: { me in
        print("\(me) began to execute ")
})

executeDispatchWorkItem(url: "www.facebook.com",completion: {data in

    switch data {
    case .success(let data):
        print("URL : \(data) completed  with \(String(describing: 
data))")
    case .failure(let error ):
        print("URL : \(error.localizedDescription) failed  with \(error.localizedDescription)")
    }

}, beganHandler: { me in
        print("\(me) began to execute ")
})
executeDispatchWorkItem(url: "www.youtube.com",completion: {data in

    switch data {
    case .success(let data):
        print("URL : \(data) completed  with \(String(describing: data))")
    case .failure(let error ):
        print("URL : \(error.localizedDescription) failed  with \(error.localizedDescription)")
    }

}, beganHandler: { me in
        print("\(me) began to execute ")
})

/// HOW EVER I WANT TO ACHIEVE SOMETHING LIKE THIS

let itemsToExecute : [DispatchWorkItem] = [dispatch1.dispatch2]

// IS THIS POSSIBLE ?

serialQueue.sync(execute: itemsToExecute) ?

回答1:

Yes, you can have an array of DispatchWorkItem objects, but to dispatch them all, you’d just have to iterate through them, e.g., with either for-in or forEach:

let queue = DispatchQueue(label: "com.domain.app.requests")
let group = DispatchGroup()

let itemsToExecute: [DispatchWorkItem] = [item1, item2]

itemsToExecute.forEach { queue.async(group: group, execute: $0) }

group.notify(queue: .main) {
    print("all done")         // this is called when the requests are done
}

Note, I used async vs sync, because the whole point of using GCD is to avoid blocking the main queue, and while sync blocks, async doesn’t.

This begs the question of why you’d bother using an array of DispatchWorkItem at all, though. Just add the tasks to the queue directly, and the queue takes care of keeping track of all of them for you.


Frankly, we’d probably just want to use URLSession. For example:

@discardableResult
func request(from urlString: String, completion: @escaping  (Result<String,Error>) -> Void) -> URLSessionTask {
    let task = URLSession.shared.dataTask(with: URL(string: urlString)!) { data, response, error in
        guard let data = data, error == nil else {
            completion(.failure(error!))
            return
        }

        guard
            let httpResponse = response as? HTTPURLResponse,
            200..<300 ~= httpResponse.statusCode
        else {
            completion(.failure(NetworkError.invalidResponse(data, response)))
            return
        }

        guard let string = String(data: data, encoding: .utf8) else {
            completion(.failure(NetworkError.nonStringBody))
            return
        }

        completion(.success(string))
    }
    task.resume()
    return task
}

Where perhaps:

enum NetworkError: Error {
    case invalidResponse(Data, URLResponse?)
    case nonStringBody
}

Then, you can do something like:

for urlString in urlStrings {
    group.enter()
    request(from: urlString) { result in
        defer { group.leave() }

        switch result {
        case .failure(let error):
            print(urlString, error)

        case .success(let string):
            print(urlString, string.count)
        }
    }
}

group.notify(queue: .main) {
    print("all done")
}