Flutter中发送GraphQL查询后未收到响应前如何中止请求?
Flutter GraphQL 主动中止未完成查询请求实现方案
你当前使用的自定义查询函数参考:
要实现提交查询后、收到响应前主动中止的功能,直接按下面的步骤改造即可:
方案一:基于网络层真实取消(推荐)
这个方案会真正断开和服务端的网络连接,不浪费带宽,是生产环境优先选择的实现。
- 前置说明:如果你的GraphQL链路用的是
DioLink(绝大多数Flutter GraphQL项目都会用Dio替换默认http实现,方便加拦截器、日志、证书校验等能力),不需要额外改链路配置;如果当前用的是默认HttpLink,只需要把链路替换为DioLink即可,替换成本极低。 - 第一步:改造你的自定义查询函数,增加取消令牌参数
// 引入dio的取消令牌,需要在pubspec.yaml添加依赖:dio: ^5.4.0 import 'package:dio/dio.dart'; Future<QueryResult> customQuery( String query, { Map<String, dynamic>? variables, FetchPolicy? fetchPolicy, // 新增可选取消令牌参数 CancelToken? cancelToken, }) async { final QueryOptions options = QueryOptions( document: gql(query), variables: variables ?? const {}, fetchPolicy: fetchPolicy ?? FetchPolicy.networkOnly, // 将取消令牌传入请求上下文,DioLink会自动识别并绑定到请求上 context: { 'cancelToken': cancelToken, }, ); return await client.query(options); }
- 第二步:业务层调用时管理取消令牌,需要中止时触发取消
// 声明页级别的取消令牌实例 CancelToken? _queryCancelToken; /// 发起查询请求 void fetchListData() async { // 发起新请求前先取消上一次未完成的请求,避免重复请求、数据错乱 _queryCancelToken?.cancel("触发新请求,自动中止旧请求"); _queryCancelToken = CancelToken(); try { final result = await customQuery( r''' query GetList($page: Int!) { list(page: $page) { id title } } ''', variables: {"page": 1}, cancelToken: _queryCancelToken, ); // 正常响应处理 if (result.data != null && !result.hasException) { // 渲染数据逻辑 } } catch (e) { // 单独识别主动取消的异常,不需要弹出错误提示 if (e is OperationCanceledException || (e is DioException && e.type == DioExceptionType.cancel)) { print("请求已主动中止"); return; } // 其他常规错误处理:网络错误、服务端报错等 print("请求出错:$e"); } } /// 主动中止请求的触发方法,可以绑定到取消按钮点击、返回事件等 void stopRunningQuery() { _queryCancelToken?.cancel("用户主动取消请求"); _queryCancelToken = null; } @override void dispose() { // 页面销毁时必须中止未完成请求,避免内存泄漏、回调执行报错 stopRunningQuery(); super.dispose(); }
方案二:零链路改造快速实现
如果你不想改动现有GraphQL链路配置,可以用Dart异步包装的方式实现业务层的请求中止,注意这个方案不会真正断开底层网络连接,只是会中断业务层的回调执行,适合快速迭代的场景。
- 第一步:添加
async工具包依赖:async: ^2.11.0 - 第二步:用可取消异步对象包装请求
import 'package:async/async.dart'; // 声明可取消操作实例 CancelableOperation<QueryResult>? _cancelableQuery; void fetchListData() async { // 取消上一次未完成操作 _cancelableQuery?.cancel(); // 包装原有查询请求为可取消操作 _cancelableQuery = CancelableOperation.fromFuture( customQuery( r''' query GetList($page: Int!) { list(page: $page) { id title } } ''', variables: {"page": 1}, ), ); try { final result = await _cancelableQuery?.value; if (result != null && result.data != null && !result.hasException) { // 数据渲染逻辑 } } catch (e) { // 常规错误处理 } } // 主动中止调用 void stopRunningQuery() { _cancelableQuery?.cancel(); _cancelableQuery = null; } @override void dispose() { stopRunningQuery(); super.dispose(); }
补充:如果是Mutation请求需要中止,改造逻辑和上面完全一致,只需要把client.query换成client.mutate,QueryOptions换成MutationOptions即可。
内容的提问来源于stack exchange,提问作者moein
相关产品推荐
相关产品推荐

