Flutter中GRPC任意一元请求的通用取消方案求助
Flutter gRPC一元请求取消实现方案(适配MVVM+GetIt架构)
1. 创建自定义取消令牌类
模仿Dio的CancelToken实现gRPC专属的取消令牌,用于传递取消信号:
class GrpcCancelToken { Completer<void>? _completer; bool get isCancelled => _completer?.isCompleted ?? false; void cancel([String? reason]) { if (!isCancelled) { _completer?.completeError(CancelledException(reason ?? "Request cancelled")); } } Future<void> get whenCancelled => _completer?.future ?? Future.value(); GrpcCancelToken() { _completer = Completer<void>(); } } // 自定义取消异常,用于仓库层捕获转换 class CancelledException implements Exception { final String message; CancelledException(this.message); @override String toString() => message; }
2. 扩展错误类型
在你的CustomError中新增取消类型,方便视图层识别:
enum ErrorType { network, grpc, cancelled, // 新增取消错误类型 // 其他已有错误类型 } class CustomError { final String message; final ErrorType type; CustomError({required this.message, required this.type}); }
3. 修改数据源层
在数据源方法中添加GrpcCancelToken参数,用CancelableOperation包装gRPC请求并绑定取消信号:
abstract class ExampleDataSource { Future<void> someMethod({required GrpcCancelToken cancelToken}); } @LazySingleton(as: ExampleDataSource) class ExampleDataSourceImpl extends ExampleDataSource { final SomeGrpcClient client = SomeGrpcClient( someClientChannel.initializeWithHostAndPort(address: "address")!, interceptors: [interceptor], ); @override Future<void> someMethod({required GrpcCancelToken cancelToken}) async { return await asyncFunctionWrapper.handleAsyncNetworkCall(() async { // 用CancelableOperation包装gRPC请求 final operation = CancelableOperation.fromFuture( client.someGrpcRequest(), // 可选:如果生成的gRPC方法返回ClientCall,可在这里主动调用cancel // onCancel: () => client.someGrpcRequest().cancel(), ); // 监听取消令牌,触发请求取消 cancelToken.whenCancelled.then((_) => operation.cancel()); // 等待请求结果或取消,取消时抛出异常 final result = await operation.valueOrCancellation(); if (result == null) throw CancelledException("Request cancelled"); return result; }); } }
4. 修改仓库层
传递取消令牌并捕获CancelledException,转换为自定义错误类型:
abstract class ExampleRepo { Future<Either<CustomError, void>> someMethod({required GrpcCancelToken cancelToken}); } @LazySingleton(as: ExampleRepo) class ExampleRepoImpl extends ExampleRepo { final ExampleDataSource exampleDs; ExampleRepoImpl(this.exampleDs); @override Future<Either<CustomError, void>> someMethod({required GrpcCancelToken cancelToken}) async { return await customRepositoryWrapper.wrapError(function: () async { try { await exampleDs.someMethod(cancelToken: cancelToken); return const Right(null); } on CancelledException catch (e) { // 将取消异常转换为自定义错误 return Left(CustomError(message: e.message, type: ErrorType.cancelled)); } }); } }
5. 修改服务层
传递取消令牌,保持层间职责清晰:
abstract class ExampleService { Future<Either<CustomError, void>> someMethod({required GrpcCancelToken cancelToken}); } @LazySingleton(as: ExampleService) class ExampleServiceImpl extends ExampleService { final ExampleRepo exampleRepo; ExampleServiceImpl(this.exampleRepo); @override Future<Either<CustomError, void>> someMethod({required GrpcCancelToken cancelToken}) async { return await exampleRepo.someMethod(cancelToken: cancelToken); } }
6. 视图模型层使用
像Dio一样创建和管理取消令牌,支持重复请求自动取消旧请求:
class ExampleViewModel extends ViewModel { final ExampleService _exampleService = GetIt.I.get<ExampleService>(); GrpcCancelToken? _currentCancelToken; Future<void> callSomeMethod() async { // 取消当前正在进行的请求(如果有) _currentCancelToken?.cancel("New request initiated"); // 创建新的取消令牌 _currentCancelToken = GrpcCancelToken(); final result = await _exampleService.someMethod(cancelToken: _currentCancelToken!); result.fold( (error) { if (error.type == ErrorType.cancelled) { // 处理请求取消的业务逻辑 print("Request cancelled: ${error.message}"); } else { // 处理其他错误 } }, (_) { // 请求成功后的逻辑 }, ); } // 页面销毁时自动取消请求 @override void onClose() { _currentCancelToken?.cancel("View model disposed"); super.onClose(); } }
内容的提问来源于stack exchange,提问作者Dev-Enoch
相关产品推荐
相关产品推荐

