@@ -21,19 +21,29 @@ import 'package:graphql/src/scheduler/scheduler.dart';
2121import 'package:graphql/src/core/_query_write_handling.dart' ;
2222
2323typedef DeepEqualsFn = bool Function (dynamic a, dynamic b);
24+ typedef AsyncDeepEqualsFn = Future <bool > Function (dynamic a, dynamic b);
2425
25- /// The equality function used for comparing cached and new data.
26+ /// The equality function used for comparing cached and new data
27+ /// in synchronous contexts like operator overrides or custom logic.
2628///
2729/// You can alternatively provide [optimizedDeepEquals] for a faster
28- /// equality check. Or provide your own via [GqlClient] constructor.
30+ /// equality check, or provide your own via the [GqlClient] constructor.
2931DeepEqualsFn gqlDeepEquals = const DeepCollectionEquality ().equals;
3032
33+ /// The async equality function used for comparing cached and new data
34+ /// during asynchronous operations like rebroadcast checks.
35+ ///
36+ /// Can be provided via the constructor for custom or isolate-based comparison logic.
37+ AsyncDeepEqualsFn gqlAsyncDeepEquals =
38+ (dynamic a, dynamic b) async => gqlDeepEquals (a, b);
39+
3140class QueryManager {
3241 QueryManager ({
3342 required this .link,
3443 required this .cache,
3544 this .alwaysRebroadcast = false ,
3645 DeepEqualsFn ? deepEquals,
46+ AsyncDeepEqualsFn ? asyncDeepEquals,
3747 bool deduplicatePollers = false ,
3848 this .requestTimeout = const Duration (seconds: 5 ),
3949 }) {
@@ -44,12 +54,15 @@ class QueryManager {
4454 if (deepEquals != null ) {
4555 gqlDeepEquals = deepEquals;
4656 }
57+ if (asyncDeepEquals != null ) {
58+ gqlAsyncDeepEquals = asyncDeepEquals;
59+ }
4760 }
4861
4962 final Link link;
5063 final GraphQLCache cache;
5164
52- /// Whether to skip deep equality checks in [maybeRebroadcastQueries ]
65+ /// Whether to skip deep equality checks in [maybeRebroadcastQueriesAsync ]
5366 final bool alwaysRebroadcast;
5467
5568 /// The timeout for resolving a query
@@ -154,7 +167,7 @@ class QueryManager {
154167 )),
155168 ))
156169 .map ((QueryResult <TParsed > queryResult) {
157- maybeRebroadcastQueries ();
170+ maybeRebroadcastQueriesAsync ();
158171 return queryResult;
159172 });
160173 } catch (ex, trace) {
@@ -170,19 +183,19 @@ class QueryManager {
170183
171184 Future <QueryResult <TParsed >> query <TParsed >(
172185 QueryOptions <TParsed > options) async {
173- final results = fetchQueryAsMultiSourceResult (_oneOffOpId, options);
186+ final results = await fetchQueryAsMultiSourceResult (_oneOffOpId, options);
174187 final eagerResult = results.eagerResult;
175188 final networkResult = results.networkResult;
176189 if (options.fetchPolicy != FetchPolicy .cacheAndNetwork ||
177190 eagerResult.isLoading) {
178191 final result = networkResult ?? eagerResult;
179192 await result;
180- maybeRebroadcastQueries ();
193+ maybeRebroadcastQueriesAsync ();
181194 return result;
182195 }
183- maybeRebroadcastQueries ();
196+ maybeRebroadcastQueriesAsync ();
184197 if (networkResult is Future <QueryResult <TParsed >>) {
185- networkResult.then ((value) => maybeRebroadcastQueries ());
198+ networkResult.then ((value) => maybeRebroadcastQueriesAsync ());
186199 }
187200 return eagerResult;
188201 }
@@ -205,7 +218,7 @@ class QueryManager {
205218 }
206219
207220 /// wait until callbacks complete to rebroadcast
208- maybeRebroadcastQueries ();
221+ maybeRebroadcastQueriesAsync ();
209222
210223 return result;
211224 }
@@ -423,11 +436,12 @@ class QueryManager {
423436 @experimental
424437 Future <List <QueryResult <Object ?>?>> refetchSafeQueries () async {
425438 rebroadcastLocked = true ;
439+ final queriesSnapshot = List .of (queries.values);
426440 final results = await Future .wait (
427- queries.values .where ((q) => q.isRefetchSafe).map ((q) => q.refetch ()),
441+ queriesSnapshot .where ((q) => q.isRefetchSafe).map ((q) => q.refetch ()),
428442 );
429443 rebroadcastLocked = false ;
430- maybeRebroadcastQueries ();
444+ maybeRebroadcastQueriesAsync ();
431445 return results;
432446 }
433447
@@ -444,7 +458,7 @@ class QueryManager {
444458
445459 /// Add a result to the [ObservableQuery] specified by `queryId` , if it exists.
446460 ///
447- /// Will [maybeRebroadcastQueries ] from [ObservableQuery.addResult] if the [cache] has flagged the need to.
461+ /// Will [maybeRebroadcastQueriesAsync ] from [ObservableQuery.addResult] if the [cache] has flagged the need to.
448462 ///
449463 /// Queries are registered via [setQuery] and [watchQuery]
450464 void addQueryResult <TParsed >(
@@ -504,10 +518,10 @@ class QueryManager {
504518 /// **Note on internal implementation details**:
505519 /// There is sometimes confusion on when this is called, but rebroadcasts are requested
506520 /// from every [addQueryResult] where `result.isNotLoading` as an [OnData] callback from [ObservableQuery] .
507- bool maybeRebroadcastQueries ({
521+ Future < bool > maybeRebroadcastQueriesAsync ({
508522 ObservableQuery <Object ?>? exclude,
509523 bool force = false ,
510- }) {
524+ }) async {
511525 if (rebroadcastLocked && ! force) {
512526 return false ;
513527 }
@@ -522,7 +536,11 @@ class QueryManager {
522536 // to [readQuery] for it once.
523537 final Map <Request , QueryResult <Object ?>> diffQueryResultCache = {};
524538 final Map <Request , bool > ignoreQueryResults = {};
525- for (final query in queries.values) {
539+
540+ final List <ObservableQuery <Object ?>> queriesSnapshot =
541+ List .of (queries.values);
542+
543+ for (final query in queriesSnapshot) {
526544 final Request request = query.options.asRequest;
527545 final cachedQueryResult = diffQueryResultCache[request];
528546 if (query == exclude || ! query.isRebroadcastSafe) {
@@ -545,7 +563,7 @@ class QueryManager {
545563 query.options.asRequest,
546564 optimistic: query.options.policies.mergeOptimisticData,
547565 );
548- if (_cachedDataHasChangedFor (query, cachedData)) {
566+ if (await _cachedDataHasChangedForAsync (query, cachedData)) {
549567 // The data has changed
550568 final queryResult = QueryResult (
551569 data: cachedData,
@@ -567,14 +585,19 @@ class QueryManager {
567585 return true ;
568586 }
569587
570- bool _cachedDataHasChangedFor (
588+ Future < bool > _cachedDataHasChangedForAsync (
571589 ObservableQuery <Object ?> query,
572590 Map <String , dynamic >? cachedData,
573- ) =>
574- cachedData != null &&
575- query.latestResult != null &&
576- (alwaysRebroadcast ||
577- ! gqlDeepEquals (query.latestResult! .data, cachedData));
591+ ) async {
592+ if (cachedData == null || query.latestResult == null ) return false ;
593+ if (alwaysRebroadcast) return true ;
594+
595+ final isEqual = await gqlAsyncDeepEquals (
596+ query.latestResult! .data,
597+ cachedData,
598+ );
599+ return ! isEqual;
600+ }
578601
579602 void setQuery (ObservableQuery <Object ?> observableQuery) {
580603 queries[observableQuery.queryId] = observableQuery;
0 commit comments