最近接到一個需求,在載入一個畫面時要同時呼叫兩支API,分別得到result之後再將兩組資料二合為一。
這邊先不討論為什麼backend不能乾淨俐落的用一支API回傳就好,正所謂無風不起浪,事出必有因,總之要怎麼在確認兩支API都得到result之後再做後續的處理呢?
你可以先call一支API然後把另外一支包在裡面,如果有三支,就再包一支,最後程式碼看起來會長這樣:
{{{{{}}}}}
不知不覺寫了一段俄羅斯娃娃呢
優雅的擁抱GCD
GCD裡面提供的Dispatch Group可以在多個Dispatch Queue執行完之後,透過dispatch_group_notify來獲得通知
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ NSLog(@"Queue One"); }); dispatch_group_async(group, queue, ^{ NSLog(@"Queue Two"); }); dispatch_group_async(group, queue, ^{ NSLog(@"Queue Three"); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"Queues Done"); });
但若你的任務是一個非同步的網路請求呢?若將async的API request包在dispatch_group_async中,你會發現在API還沒接到result時dispatch_group_notify就接到通知了,這時咱先別急著把巢狀怪物搬回來交差了事,Dispatch Group還提供了兩個法寶:dispatch_group_enter與dispatch_group_leave
dispatch_group_enter & dispatch_group_leave
dispatch_group_enter這個方法用來計算group中的任務數,也就是說呼叫一次任務數就會+1,而dispatch_group_leave則相對的對任務數-1,因此在每個API request發出時下dispatch_group_enter,並在得到回傳結果時下dispatch_group_leave,當任務數為0時才會收到通知,感覺有點像ARC的retain count的概念。
因此就可以寫成:
Objc
dispatch_group_t group = dispatch_group_create(); dispatch_group_enter(group); @weakify(self); [API request1: ^(APIStatus status, NSArray *result) { NSLog(@"Group1"); dispatch_group_leave(group); }]; dispatch_group_enter(group); [API request2:^(APIStatus status, NSArray *result) { NSLog(@"Group2"); dispatch_group_leave(group); }]; dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"Done"); });
Swift
let dispatchGroup = DispatchGroup() dispatchGroup.enter() API.request1 { print("group1") dispatchGroup.leave() } dispatchGroup.enter() API.request2 { print("group2") dispatchGroup.leave() } dispatchGroup.notify(queue: .main) { print("Done") }
這樣是不是比俄羅斯娃娃般的巢狀連環摳優雅多了呢~