问题
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")
}
来源:https://stackoverflow.com/questions/57137326/is-there-a-specific-way-to-append-dispatchworkitems-to-a-dispatchqueue-instead-o