Dart/Flutter如何防止异步函数被并发重复调用?
解决Dart异步函数重复调用的问题
问题背景
我在Flutter中有一个不能被同时调用两次的异步函数,示例代码如下:
class DatabaseHelper { Future<void> add100elements() async { // 从数据库查询并添加100条数据到本地列表 var elementsToAdd = await db.rawQuery('select * from quotes where id > ? limit 100', this.currentId); this.localList.addAll(elementsToAdd); this.currentId += 100; } }
应用中可能有两处几乎同时调用add100elements,这会导致重复执行,添加相同的100条数据。我自己实现了一个手动的解决方案:
class DatabaseHelper { bool isRunning = false; Future<void> add100elements() async { if (isRunning) return; isRunning = true; var elementsToAdd = await db.rawQuery('select * from quotes where id > ? limit 100', this.currentId); this.localList.addAll(elementsToAdd); this.currentId += 100; isRunning = false; } }
但这种方式比较繁琐,想知道Dart有没有内置的实现方式或者更优的解决方案。
更优雅的手动改进方案
你的布尔标记方案存在一个隐患:如果函数执行过程中抛出异常,isRunning会一直保持true,导致后续所有调用都无法执行。可以用try/finally块修复这个问题:
class DatabaseHelper { bool isRunning = false; Future<void> add100elements() async { if (isRunning) return; isRunning = true; try { var elementsToAdd = await db.rawQuery('select * from quotes where id > ? limit 100', this.currentId); this.localList.addAll(elementsToAdd); this.currentId += 100; } finally { // 无论成功还是失败,都重置标记 isRunning = false; } } }
如果希望后续调用不是直接返回,而是等待前一次执行完成后再执行(即任务排队),可以用一个Future变量跟踪当前执行的任务,实现更健壮的控制:
class DatabaseHelper { Future<void>? _currentTask; Future<void> add100elements() async { // 如果有正在执行的任务,先等待它完成 if (_currentTask != null) { await _currentTask; return; } // 标记当前执行的任务 _currentTask = _performAdd(); try { await _currentTask; } finally { // 执行完成后清空标记 _currentTask = null; } } // 把实际逻辑抽离成单独方法,让代码更清晰 Future<void> _performAdd() async { var elementsToAdd = await db.rawQuery('select * from quotes where id > ? limit 100', this.currentId); this.localList.addAll(elementsToAdd); this.currentId += 100; } }
使用社区同步锁库
Dart标准库没有内置的互斥锁,但可以使用社区维护的synchronized包,它提供的Mutex类能简洁实现临界区控制,自动处理锁的获取和释放:
import 'package:synchronized/synchronized.dart'; class DatabaseHelper { final _mutex = Mutex(); Future<void> add100elements() async { // 同一时间只有一个调用能进入这个代码块 await _mutex.synchronized(() async { var elementsToAdd = await db.rawQuery('select * from quotes where id > ? limit 100', this.currentId); this.localList.addAll(elementsToAdd); this.currentId += 100; }); } }
这种方式无需手动管理状态,即使执行中抛出异常,锁也会自动释放,代码更简洁可靠。
内容的提问来源于stack exchange,提问作者DevShark
相关产品推荐
相关产品推荐

