如何在完成块之前解决嵌套的异步调用
How to get nested asynchronous calls resolved before completion block
这是设置。我有一个方法,它有一个完成块,我想在其中 return 一个 Item
的列表。这些 Item
是从 API 中提取的。我想让每个提取异步发生,但最终 return Item
全部在一起。
这是我的:
public static func fetchItems(numberOfItems: Int, completion: ([Item]?, NSError?) -> ()) -> Void {
var items: [Item] = []
let group = dispatch_group_create()
for (var itemId = 0; itemId < numberOfItems; itemId++) {
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
APIManager.fetchItemWithId(itemId) {
(item, error) in
guard let item = item else {
// handle error
}
print("Item \(itemId) downloaded")
items.append(item)
}
}
}
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
completion(items, nil)
}
}
我的输出结果是:
nil
Item 0 downloaded
Item 1 downloaded
Item 2 downloaded
etc
虽然我正在异步调度 Item
的调用,但调用本身在内部有另一个异步操作 - 在示例中由 APIManager.fetchItemWithId
说明。因此,最终,我的 completion
在 API 请求解决之前被命中。
我在这里错过了什么?
您的问题在于对 APIManager
的异步调用。您的块,在该调用中的块之前分派给组完成。实际上,组中的 all 个块在它之前完成。如果您可以选择调用 fetchItemWithId
的同步版本 - 请在此处使用。如果不是 - 使用 dispatch_semaphore_t
.
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
APIManager.fetchItemWithId(itemId) {
(item, error) in
guard let item = item else {
// handle error
dispatch_semaphore_signal(semaphore);
}
print("Item \(itemId) downloaded")
items.append(item)
dispatch_semaphore_signal(semaphore);
}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
如有不明之处,欢迎随时提问。或者,如果我误解了你的意图
更新
我决定添加一些注释以使执行流程清楚为什么一切都按照它的方式发生
for (var itemId = 0; itemId < numberOfItems; itemId++) {
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
//1 reach this point for "numberOfItems" times
APIManager.fetchItemWithId(itemId) {
(item, error) in
guard let item = item else {
// handle error
}
//4 we have no guarantee, when this point will be reached relatively to execution flow of "fetchItems" method.
//Actually, looks like it is dispatched to some low priority background queue.
//When it is first reached, "group" blocks have already been dispatched and successfully executed
print("Item \(itemId) downloaded")
items.append(item)
}
//2 previous block has been added to some queue. Reach this point for "numberOfItems" times
}
}
//3 reach this point. Most likely all group blocks have already been executed, so completion block is dispatched almost immediately
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
completion(items, nil)
}
这是设置。我有一个方法,它有一个完成块,我想在其中 return 一个 Item
的列表。这些 Item
是从 API 中提取的。我想让每个提取异步发生,但最终 return Item
全部在一起。
这是我的:
public static func fetchItems(numberOfItems: Int, completion: ([Item]?, NSError?) -> ()) -> Void {
var items: [Item] = []
let group = dispatch_group_create()
for (var itemId = 0; itemId < numberOfItems; itemId++) {
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
APIManager.fetchItemWithId(itemId) {
(item, error) in
guard let item = item else {
// handle error
}
print("Item \(itemId) downloaded")
items.append(item)
}
}
}
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
completion(items, nil)
}
}
我的输出结果是:
nil
Item 0 downloaded
Item 1 downloaded
Item 2 downloaded
etc
虽然我正在异步调度 Item
的调用,但调用本身在内部有另一个异步操作 - 在示例中由 APIManager.fetchItemWithId
说明。因此,最终,我的 completion
在 API 请求解决之前被命中。
我在这里错过了什么?
您的问题在于对 APIManager
的异步调用。您的块,在该调用中的块之前分派给组完成。实际上,组中的 all 个块在它之前完成。如果您可以选择调用 fetchItemWithId
的同步版本 - 请在此处使用。如果不是 - 使用 dispatch_semaphore_t
.
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
APIManager.fetchItemWithId(itemId) {
(item, error) in
guard let item = item else {
// handle error
dispatch_semaphore_signal(semaphore);
}
print("Item \(itemId) downloaded")
items.append(item)
dispatch_semaphore_signal(semaphore);
}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
如有不明之处,欢迎随时提问。或者,如果我误解了你的意图
更新
我决定添加一些注释以使执行流程清楚为什么一切都按照它的方式发生
for (var itemId = 0; itemId < numberOfItems; itemId++) {
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
//1 reach this point for "numberOfItems" times
APIManager.fetchItemWithId(itemId) {
(item, error) in
guard let item = item else {
// handle error
}
//4 we have no guarantee, when this point will be reached relatively to execution flow of "fetchItems" method.
//Actually, looks like it is dispatched to some low priority background queue.
//When it is first reached, "group" blocks have already been dispatched and successfully executed
print("Item \(itemId) downloaded")
items.append(item)
}
//2 previous block has been added to some queue. Reach this point for "numberOfItems" times
}
}
//3 reach this point. Most likely all group blocks have already been executed, so completion block is dispatched almost immediately
dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
completion(items, nil)
}