在开发中我们常常会遇到在一个界面中需要异步请求多个接口,通过不同接口的返回数据刷新界面不同控件的需求。这种情况下有两种选择,一种是 依次请求多个接口,分别拿到数据 这种方式有两种方法实现:1: 接口回调嵌套(容易造成代码金字塔,写法low)2:管理多线程 使异步的请求操作变成依次执行。 依次请求接口的方式很容易造成用户在当前界面请求时间过长而影响体验,这就需要我们 并发的发起多个异步请求了,但有时候我们界面刷新时 不同界面间的数据存在 关联,我们得在所有请求完成后才能 统一 对界面刷新或者其它操作, 下面我简单介绍使用GCD 使 监听异步请求全部完成 和 异步请求依次进行 的方式。
1. 监听异步请求全部完成 再完成刷新界面等操作
先上代码:
dispatch_group_t group = dispatch_group_create();//创建队列组
dispatch_group_enter(group); //队列组中加入一个异步操作
//网络请求
[[HttpTool sharedJsonClient] requestJsonDataWithPath:newUrlString andBlock:^(id _Nonnull data, NSError * _Nonnull error) {
NSLog(@"请求第一个完成");
//队列组中移除一个异步操作
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[[HttpTool sharedJsonClient] requestJsonDataWithPath:newUrlString andBlock:^(id _Nonnull data, NSError * _Nonnull error) {
NSLog(@"请求第二个完成");
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[[HttpTool sharedJsonClient] requestJsonDataWithPath:newUrlString andBlock:^(id _Nonnull data, NSError * _Nonnull error) {
NSLog(@"请求第三个完成");
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"请求全部完成 w回到主线程了");
});
使用了CGD 中队列组方式,通过发起请求时 加入一个异步事件到队列组,请求完成后 将其移除, 最终监听队列组中无操作时 即为所有请求事件已完成。 要注意的一点是 网络请求本身就是异步的操作, 所以不能写成:
dispatch_async(queue, ^{
[[HttpTool sharedJsonClient] requestJsonDataWithPath:newUrlString andBlock:^(id _Nonnull data, NSError * _Nonnull error) {
NSLog(@"请求第一个完成");
dispatch_semaphore_signal(semaphore); //信号量 +1
}];
}
2. 使异步的网络请求依次进行
//新建一个串行(并行 都可以)队列, 在该 队列中对 所有 的网络请求做操作
dispatch_queue_t queue = dispatch_queue_create("sdsdsdsd", DISPATCH_QUEUE_SERIAL);
//创建信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); //初始为 0
NSLog(@"开始执行3");
dispatch_async(queue, ^{
NSLog(@"开始执行1");
[[HttpTool sharedJsonClient] requestJsonDataWithPath:newUrlString andBlock:^(id _Nonnull data, NSError * _Nonnull error) {
NSLog(@"请求第一个完成");
dispatch_semaphore_signal(semaphore); //信号量 +1
}];
//信号量为0 时不 执行 (如果信号量 不为 0 时才往下执行并且 信号量 -1)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"开始执行2");
[[HttpTool sharedJsonClient] requestJsonDataWithPath:newUrlString andBlock:^(id _Nonnull data, NSError * _Nonnull error) {
NSLog(@"请求第二个完成");
dispatch_semaphore_signal(semaphore); //信号量 +1
}];
//信号量为0 时不 执行 (如果信号量 不为 0 时才往下执行并且 信号量 -1)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"开始执行3");
[[HttpTool sharedJsonClient] requestJsonDataWithPath:newUrlString andBlock:^(id _Nonnull data, NSError * _Nonnull error) {
NSLog(@"请求第三个完成");
dispatch_async(queue, ^{
dispatch_semaphore_signal(semaphore); //信号量 +1
});
}];
//信号量为0 时不 执行 (如果信号量 不为 0 时才往下执行并且 信号量 -1)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"在当前线程中继续执行");
});
需要注意的事项是:
- 如果在 主队列中 做 信号量的加减和wait等操作 ,dispatch_semaphore_wait 方式会阻塞当前线程(主线程),而网络请求的回调默认是回到主线程中 处理事件的, 由此会导致 主线程中 dispatch_semaphore_signal(semaphore); 方法不会执行,造成主线程死锁。 所以,只能新建一个 串行或者 并行 队列(新建队列的时候会开一条线程管理事件) 对 网络请求操作 做管理 (不能在主队列)
- 使用AFN网络请求操作会自动开启子线程执行,而请求完成后的操作会自动回到主线程。