You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 07:10:56