- 實現網絡請求順序執行的幾種方案及優缺點比較
- 網絡請求順序執行的具體實現
實現網絡請求順序執行的方案
- 回調中發起下次請求
- 優點:最簡單
- 缺點:會產生回調地獄的問題?;卣{套回調。
- dispatch_group
-
dispatch_group
的本質實現還是通過的信號量機制,所以優缺點與信號量方式基本是一樣的。只是API更加方便一些。
-
- 信號量
- 優點:系統API即可完成,無需第三方支持,不會產生回調地獄問題,是通過調度線程完成的。
- 缺點:當請求的回調與wait在同一串行隊列的時候會發生死鎖。
- PromiseKit
- 優點:鏈式編程,代碼可讀性較高,本質和回調方式是一樣的。
- 缺點:需要導入PromiseKit第三方庫
- 待補充...
代碼實現
請求的發起如下
- (void) requestOneWithSuccessBlock:(void(^)(void))successBlock {
AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager manager];
sessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
sessionManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"application/zip", @"text/json", @"text/javascript", @"text/html", @"text/plain", nil];
NSLog(@"%@",R1_START);
[sessionManager GET:@"http://www.weather.com.cn/data/cityinfo/101190408.html" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@",R1_END);
if (successBlock) {
successBlock();
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
}
- (void) requestTwoWithBlock:(void(^)(void))successBlock{
AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager manager];
sessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
sessionManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"application/zip", @"text/json", @"text/javascript", @"text/html", @"text/plain", nil];
NSLog(@"%@", R2_START);
[sessionManager GET:@"http://wthrcdn.etouch.cn/weather_mini?city=%E5%8C%97%E4%BA%AC%E5%B8%82" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@", R2_END);
if (successBlock) {
successBlock();
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
}
信號量的方式實現
CODE
/*通過信號量的方式實現順序執行*/
- (void)serialBySemaphore {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self requestOneWithSuccessBlock:^{
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[self requestTwoWithBlock:^{
}];
});
}
CODE ANALYSIS
使用信號量需要注意,dispatch_semaphore_wait()
方法是會阻塞當前線程,如果沒有接收到信號量,就一直阻塞當前線程的執行。
所以一定要注意網絡請求的回調是否和wait
在同一條串行隊列中。如果在同一條串行隊列則導致死鎖情況。
串行隊列的性質導致了只會有一條線程來這個隊列取任務執行,并且一個任務執行完畢之后才會取下一個任務。當請求發起之后線程就會執行wait
操作,而當請求回來之后,需要等待wait
之后才可以執行。然而wait
又需要回調方法中的signal
操作才能繼續向下執行。相互等待導致死鎖發生。
這也就是為什么在方法的開始將線程切換到子線程,AFNetworking
的回調方法如果沒有指定completionQueue
則默認提交到在主隊列,也就是在主線程執行,而將信號量相關操作切換到子線程之后,阻塞的就是這條子線程,等到請求完成之后,主線程執行回調方法,釋放信號量,這條子線程接收到信號量,繼續向下執行,發起下一個請求。
信號量的三個方法介紹
dispatch_semaphore_create(0)
創建一個值為0信號量
dispatch_semaphore_signal(semaphore)
將信號量semaphore
的值增加1
dispatch_semaphore_wait(semaphore,time)
, 阻塞線程的執行,等待信號量semaphore
,只有信號量的值大于0的時候才向下執行。
dispatch_group
方式實現
CODE ONE
-(void) serialByGroupWait {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self requestOneWithSuccessBlock:^{
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_group_enter(group);
[self requestTwoWithBlock:^{
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"all request done!");
});
});
}
這種方式的實現方式的原理與信號量相似,只是API不同。
CODE TWO
- (void) serialByGroupNotify {
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self requestOneWithSuccessBlock:^{
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
[self requestTwoWithBlock:^{
}];
});
}
這種方式的不同就是講第二個請求放到了組內任務完成的通知方法中。
當group中的所有任務都完成了,會執行notify
方法。本質其實也是在監聽這個任務組中的信號量是否都已完成。
回調中執行的方式
CODE
- (void) serialByCallBack {
[self requestOneWithSuccessBlock:^{
[self requestTwoWithBlock:^{
}];
}];
}
DEMO
https://github.com/cocacola-ty/demos/tree/master/SerialNetRequest
作者:tianyu_f
鏈接:http://www.lxweimin.com/p/b1f963554489
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權并注明出處。