我们知道在flutter中可以通过Future.wait
,来进行异步并发请求,并且在所有异步任务执行完后,再进行后续代码的执行,但是问题在于Future.wait
会将异步任务同时发起,将会产生可能意想不到的问题,比如在异步函数中,通过channel调用原生函数,原生函数需要创建线程来完成,这时大量的线程创建是否会对app制造压力,所以想着是否可以控制最大并发数量,就像是iOS原生中的信号量来控制最大并发呢
其实这里可以简单的先理清一下需求,就是一个任务执行完后检查一下是否还有任务,如果有,并且不超过要求的最大的并发量,就执行
所以这里不可以像Future.wait
一样将Future
传进来,而应该是Future Function()
,并且需要设置最大的并发量
static Future test(List<Future Function()> list,{int maxAsyncCount = 10}) async {
}
-
下一步需要考虑的是如何监听任务执行完毕,我这里用到
ValueNotifier
,来监听当前任务执行的数量
static Future test(List<Future Function()> list,{int maxAsyncCount = 10}) async {
ValueNotifier doingCount = ValueNotifier(0);
}
-
接下来需要监听这个notifier
static Future wait(List<Future Function()> list,{int maxAsyncCount = 10}) async {
ValueNotifier doingCount = ValueNotifier(0);
doingCount.addListener(() {
if (doingCount.value < maxAsyncCount && list.isNotEmpty) {
doTask(list.first);
doingCount.value++;
}
});
}
这里封装了执行任务的方法
doTask(Future Function() task) async {
list.remove(task);
await task();
doingCount.value--;
}
作为wait
内部函数,这样可以获取到list和doingCount
static Future wait(List<Future Function()> list,{int maxAsyncCount = 10}) async {
ValueNotifier doingCount = ValueNotifier(0);
doTask(Future Function() task) async {
list.remove(task);
await task();
doingCount.value--;
}
doingCount.addListener(() {
if (doingCount.value < maxAsyncCount && list.isNotEmpty) {
doTask(list.first);
doingCount.value++;
}
});
}
每当有新任务执行,doingCount
进行+1操作,每当有任务执行完成进行-1操作
-
最后一步是所有任务执行完成后返回,用到了
Completer
,再所有任务开始执行前进行等待future
,当最后一个任务执行完成后调用complete
static Future wait(List<Future Function()> list,{int maxAsyncCount = 10}) async {
if (list.isEmpty) return;
ValueNotifier doingCount = ValueNotifier(0);
Completer completer = Completer();
doTask(Future Function() task) async {
list.remove(task);
await task();
doingCount.value--;
}
doingCount.addListener(() {
if (doingCount.value < maxAsyncCount && list.isNotEmpty) {
doTask(list.first);
doingCount.value++;
} else if (doingCount.value == 0) {
completer.complete();
}
});
doTask(list.first);
doingCount.value++;
await completer.future;
}
这里需要手动去出发第一个任务的执行
到这里基本的功能就实现了,我们可以进行一个简单的测试
final date = DateTime.now();
AsyncCountFuture.wait(
List.generate(10, (index) {
return () async {
await Future.delayed(Duration(seconds: index % 2 == 0 ? 1 : 4));
print("$index 延迟${(DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch)~/1000}秒执行");
};
}),
maxAsyncCount: 5,
).then((value) {
print("执行完毕");
});
打印结果
flutter: 0 延迟1秒执行
flutter: 2 延迟1秒执行
flutter: 4 延迟1秒执行
flutter: 6 延迟2秒执行
flutter: 8 延迟3秒执行
flutter: 1 延迟4秒执行
flutter: 3 延迟4秒执行
flutter: 5 延迟5秒执行
flutter: 7 延迟5秒执行
flutter: 9 延迟7秒执行
flutter: 执行完毕
perfect,确实是我们想要的结果
但是还存在一个问题,就是入参的Future Function()
被定义死了,这个地方我优化的方案,借鉴了ListView,builder的方式,提供了itemCount
,和itemBuilder
,将Future
函数的执行放到业务端来处理
static Future waitBuilder({
required int itemCount,
required FutureCallback itemBuilder,
int maxAsyncCount = 10,
}) async {
if (itemCount == 0) return;
ValueNotifier doingCount = ValueNotifier(0);
int nextIndex = 0;
Completer completer = Completer();
doTask(int index) async {
nextIndex++;
await itemBuilder(index);
doingCount.value--;
}
doingCount.addListener(() {
if (doingCount.value < maxAsyncCount && nextIndex < itemCount) {
doTask(nextIndex);
doingCount.value++;
} else if (doingCount.value == 0) {
completer.complete();
}
});
doTask(nextIndex);
doingCount.value++;
await completer.future;
}
这样,业务上要执行什么异步任务业务可以自己说了算,例如:
test() {
List<Map> dataList = List.generate(10, (index) {
return {"name":"$index号","age":"${index+20}","second":index%2 == 0?1:4};
});
AsyncCountFuture.waitBuilder(
itemCount: 10,
itemBuilder: (index) async {
final item = dataList[index];
if (index%2 == 0) {
await asyncTask1(item["name"], item["age"], item["second"]);
} else {
await asyncTask2(item["name"], item["second"]);
}
},
maxAsyncCount: 5,
).then((value) {
print("执行完毕");
});
}
asyncTask1(String name,String age,int second) async {
await Future.delayed(Duration(seconds: second));
print("task1 --- $name 年龄: $age");
}
asyncTask2(String name,int second) async {
await Future.delayed(Duration(seconds: second));
print("task2 --- $name 年龄未知");
}