如何一个一个地发出多个网络请求,这样我就不会丢失数据?
How to make multiple network requests one by one so I don't lose data?
我有以下场景 - 我有一个应用程序需要在后台从服务器异步获取一些音频文件。音频文件很大——比如 10000。它们是 1-2 秒的 mp3 文件(每个 5-10 kb)。对于这些文件中的每一个,我都必须发出网络请求并使用其 ID 获取文件。有两种类型的音频文件 - 用于您的母语和您的学习语言。所以我像这样检查所有 ID - 1. 获取 ID 为 1 的原生音频,然后获取 ID 为 1 的学习音频 ---> ID+1 并再次出现相同的场景。
问题是当我这样做时,一些音频文件最后损坏了。我想这是因为我没有正确执行异步请求。这是我的代码:
DispatchQueue.global(qos: .background).async {
let myGroup = DispatchGroup()
for category in arrCategories {
for library in category.libraries {
for group in library.groups {
for word in group.words {
myGroup.enter()
User.current.getAudioFile(forWord: word, language: User.current.nativeLanguage, isNative: true, callback: { (success) in
if success {
User.current.getAudioFile(forWord: word, language: User.current.learningLanguage, isNative: false, callback: { (success) in
if success {
myGroup.leave()
} else {
print("ERROR LEARNING")
myGroup.leave()
}
})
} else {
print("ERROR NATIVE")
myGroup.leave()
}
})
}
}
}
}
myGroup.notify(queue: DispatchQueue.main, execute: {
print("READY")
running = false
})
我的网络请求:
request(urlRequest).response { (response) in
if response.data != nil && response.error == nil {
let soundData = response.data! as NSData
let fileURL = URL(fileURLWithPath: filePath)
do {
try soundData.write(to: fileURL, options: .atomic)
callback(true)
} catch {
print(error)
callback(false)
}
} else {
print(response.error?.localizedDescription)
callback(false)
}
有人能告诉我代码有什么问题吗?因为如果我使用这种方法,有些音频文件不会播放。但是,如果我只下载这个文件而不进行这么多请求,它可以正常播放。
使用循环只是启动所有异步请求。您的问题在于处理所有并行响应。
一个选择是在完成第一个循环之前使用 mutex/semaphor 来阻止循环(我不建议这样做。
第二个选项是创建一个请求列表,当一个请求完成时开始下一个。
绝对是一份 OperationQueue and BlockOperation 的工作。使用 BlockOperation
,您甚至可以将一个依赖项添加到另一个依赖项,并确保所有项都已执行。
这是一个想法(未经测试的代码):
var queue = OperationQueue()
var previousOp: BlockOperation ? = nil
for task in tasks {
let op = BlockOperation { your code }
if previousOp != nil {
previousOp.addDepencendy(op)
}
previousOp = op
}
我有以下场景 - 我有一个应用程序需要在后台从服务器异步获取一些音频文件。音频文件很大——比如 10000。它们是 1-2 秒的 mp3 文件(每个 5-10 kb)。对于这些文件中的每一个,我都必须发出网络请求并使用其 ID 获取文件。有两种类型的音频文件 - 用于您的母语和您的学习语言。所以我像这样检查所有 ID - 1. 获取 ID 为 1 的原生音频,然后获取 ID 为 1 的学习音频 ---> ID+1 并再次出现相同的场景。
问题是当我这样做时,一些音频文件最后损坏了。我想这是因为我没有正确执行异步请求。这是我的代码:
DispatchQueue.global(qos: .background).async {
let myGroup = DispatchGroup()
for category in arrCategories {
for library in category.libraries {
for group in library.groups {
for word in group.words {
myGroup.enter()
User.current.getAudioFile(forWord: word, language: User.current.nativeLanguage, isNative: true, callback: { (success) in
if success {
User.current.getAudioFile(forWord: word, language: User.current.learningLanguage, isNative: false, callback: { (success) in
if success {
myGroup.leave()
} else {
print("ERROR LEARNING")
myGroup.leave()
}
})
} else {
print("ERROR NATIVE")
myGroup.leave()
}
})
}
}
}
}
myGroup.notify(queue: DispatchQueue.main, execute: {
print("READY")
running = false
})
我的网络请求:
request(urlRequest).response { (response) in
if response.data != nil && response.error == nil {
let soundData = response.data! as NSData
let fileURL = URL(fileURLWithPath: filePath)
do {
try soundData.write(to: fileURL, options: .atomic)
callback(true)
} catch {
print(error)
callback(false)
}
} else {
print(response.error?.localizedDescription)
callback(false)
}
有人能告诉我代码有什么问题吗?因为如果我使用这种方法,有些音频文件不会播放。但是,如果我只下载这个文件而不进行这么多请求,它可以正常播放。
使用循环只是启动所有异步请求。您的问题在于处理所有并行响应。
一个选择是在完成第一个循环之前使用 mutex/semaphor 来阻止循环(我不建议这样做。
第二个选项是创建一个请求列表,当一个请求完成时开始下一个。
绝对是一份 OperationQueue and BlockOperation 的工作。使用 BlockOperation
,您甚至可以将一个依赖项添加到另一个依赖项,并确保所有项都已执行。
这是一个想法(未经测试的代码):
var queue = OperationQueue()
var previousOp: BlockOperation ? = nil
for task in tasks {
let op = BlockOperation { your code }
if previousOp != nil {
previousOp.addDepencendy(op)
}
previousOp = op
}