Ios 发出多个异步请求,但只等待一个
我有一个关于异步请求的问题。我想从网络上的不同来源获取数据。每个来源可能都有我想要的数据,但我事先不知道。因为我只需要这些信息一次,一旦一个信息源给了我所需的数据,我就不在乎其他信息源了。我该怎么做呢? 我想用didSet做,只设置一次,类似这样:Ios 发出多个异步请求,但只等待一个,ios,swift,asynchronous,Ios,Swift,Asynchronous,我有一个关于异步请求的问题。我想从网络上的不同来源获取数据。每个来源可能都有我想要的数据,但我事先不知道。因为我只需要这些信息一次,一旦一个信息源给了我所需的数据,我就不在乎其他信息源了。我该怎么做呢? 我想用didSet做,只设置一次,类似这样: var dogPicture:dogPicture?=零{ 迪塞特{ //对这幅画做些什么 } } func findPictureOfDog(u)来源)->狗图片?{ 源中的源{ 让task=URL.Session.shared.dataTask
var dogPicture:dogPicture?=零{
迪塞特{
//对这幅画做些什么
}
}
func findPictureOfDog(u)来源)->狗图片?{
源中的源{
让task=URL.Session.shared.dataTask(带:source){(数据、响应、错误)在
//错误处理。。。
如果data.isWhatIWanted()&&dogPicture==nil{
dogPicture=data.getPicture()
}
}
task.resume()
}
}
来源=[“yahoo.com”、“google.com”、“pinterest.com”]
findPictureOfDog(来源)
但是,如果我可以等到findPictureOfDog()
完成,这将非常有帮助,因为根据我是否找到什么,我必须要求用户提供更多输入
我不知道如何用上面的方法来做,因为如果我没有找到任何东西,didSet将永远不会被调用,但我应该向用户索要一张图片
另外:iswhatiwant()
相当昂贵,因此,如果有一种方法可以在我找到一个DogPicture后中止处理程序的执行,那就太好了
我希望我说清楚了,希望有人能帮我解决这个问题
向您致以最诚挚的问候并感谢您抽出宝贵的时间如果您只关心一项任务,请使用完成处理程序,如果找不到图片,请致电
completion(nil)
var dogPicture : DogPicture?
func findPictureOfDog(_ sources, completion: @escaping (DogPicture?) -> Void) {
for source in sources {
let task = URL.Session.shared.dataTask(with: source) { (data, response, error) in
// error handling ...
if data.isWhatIWanted() && dogPicture == nil {
let picture = data.getPicture()
completion(picture)
}
}
task.resume()
}
}
sources = ["yahoo.com", "google.com", "pinterest.com"]
findPictureOfDog(sources) { [weak self] picture in
if let picture = picture {
self?.dogPicture = picture
print("picture set")
} else {
print("No picture found")
}
}
您可以使用
DispatchGroup
在所有请求返回后运行检查:
func findPictureOfDog(_ sources: [String]) -> DogPicture? {
let group = DispatchGroup()
for source in sources {
group.enter()
let task = URLSession.shared.dataTask(with: source) { (data, response, error) in
// error handling ...
if data.isWhatIWanted() && dogPicture == nil {
dogPicture = data.getPicture()
}
group.leave()
}
task.resume()
}
group.notify(DispatchQueue.main) {
if dogPicture == nil {
// all requests came back but none had a result.
}
}
}
有几件事:
返回DogPicture
,而应该使用完成处理程序模式。例如,而不是:
func findPictureOfDog(_ sources: [String]) -> DogPicture? {
...
return dogPicture
}
相反,您可能会执行以下操作:
func findPictureOfDog(_ sources: [String], completion: @escaping (Result<DogPicture, Error>) -> Void) {
...
completion(.success(dogPicture))
}
findPictureOfDog
例程中,并在完成时调用完成处理程序
我建议不要在这个过程中使用属性和它们的观察者,因为这会引发关于如何同步访问以确保线程安全等问题。完成处理程序是明确的,可以避免这些次要问题是您想要的
在计算上非常昂贵。这有两个含义:
- 如果它在计算上很昂贵,那么您可能不希望在
completionHandler中同步调用它,因为这是一个串行队列。每当处理串行队列(无论是主队列、网络会话串行队列还是任何自定义串行队列)时,您通常希望尽快进出(以便队列可以自由地继续处理其他任务) 例如,让我们想象一下,谷歌的请求最先出现,但此时您还不知道,它没有包含您想要的内容,dataTask(with:completionHandler:)
正在缓慢地检查结果。让我们想象一下,在这段时间里,雅虎的请求进来了。如果同步调用iswhatiwant
,Yahoo请求的结果将无法开始检查其结果,直到Google请求失败,因为您正在对此串行队列执行同步调用 我建议您可能希望在收到结果时开始检查,而不是等待其他结果。为此,您需要一个iswhatiwant
的格式副本,该格式副本相对于网络串行队列异步运行iswhatiwant
是一个可取消的过程吗?理想情况下是这样,因此如果雅虎镜像成功,它可以取消现在不必要的PinterestisWhatIWanted
。取消网络请求很容易,但更可能的是,我们真正想要取消的是这个昂贵的iswhatiwant
过程。但是我们不能在没有看到你在那里做什么的情况下对此发表评论 但是,让我们假设您正在通过iswhatiwant
对象执行对象分类。因此,您可能会在找到第一个匹配项后立即处理任何挂起的请求VNCoreMLRequest
操作的解决方案
因此,为了回答您的主要问题,我们的想法是编写一个例程,在源代码中进行迭代,并在第一次成功时调用主完成处理程序,同时确保防止任何后续/并发请求调用完成处理程序:
- 您可以保存对完成处理程序的本地引用
- 一旦成功找到合适的图像,您可以:
- 调用保存的完成处理程序李>
nil
您保存的引用(因此,如果您有其他请求已在大约
findPictureOfDog(sources: [String]) { result in
switch result {
case .success(let dogPicture): ...
case .failure(let error): ...
}
}
// but don’t try to access the DogPicture or Error here
func findPictureOfDog(_ sources: [String], completion: @escaping DogPictureCompletion) {
var firstCompletion: DogPictureCompletion? = completion
let synchronizationQueue: DispatchQueue = .main // note, we could have used any *serial* queue for this, but main queue is convenient
let completionOperation = BlockOperation {
synchronizationQueue.async {
// if firstCompletion not nil by the time we get here, that means none of them matched
firstCompletion?(.failure(DogPictureError.noneFound))
}
print("done")
}
for source in sources {
let url = URL(string: source)!
let operation = DogPictureOperation(url: url) { result in
if case .success(_) = result {
synchronizationQueue.async {
firstCompletion?(result)
firstCompletion = nil
Queues.shared.cancelAllOperations()
}
}
}
completionOperation.addDependency(operation)
Queues.shared.processingQueue.addOperation(operation)
}
OperationQueue.main.addOperation(completionOperation)
}