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

如何在urql Exchange中实现缓存优先并延迟更新缓存的离线延迟请求

实现Cache-and-Network类型的urql Exchange(带延迟请求与离线支持)

核心思路

要实现这类Exchange,我们需要完成三个核心逻辑:

  • 优先从缓存读取数据并立即返回
  • 延迟1秒后发起网络请求,成功后更新缓存
  • 离线状态下跳过网络请求,避免抛出错误影响用户体验

完整实现代码

import { Exchange, makeOperation, OperationResult } from '@urql/core';
import { delay, from, switchMap, catchError, of, concat } from 'rxjs';

export const cacheAndNetworkWithDelayExchange: Exchange = ({ forward, cache }) => {
  return ops$ => {
    return ops$.pipe(
      switchMap(operation => {
        // 1. 读取缓存数据,生成初始响应流
        const cachedData = cache.readQuery(operation.query, operation.variables);
        const initialCacheResult$ = cachedData 
          ? of(makeOperation(operation.kind, operation, { data: cachedData })) 
          : of();

        // 2. 延迟1秒后的网络请求处理流
        const delayedNetworkRequest$ = from(Promise.resolve()).pipe(
          delay(1000),
          switchMap(() => {
            // 离线状态下直接跳过网络请求
            if (!navigator.onLine) {
              return of();
            }

            // 发起网络请求并处理结果
            return forward(operation).pipe(
              switchMap((result: OperationResult) => {
                // 请求成功时更新缓存
                if (result.data) {
                  cache.writeQuery(operation.query, operation.variables, result.data);
                }
                return of(result);
              }),
              // 捕获网络错误,避免错误覆盖缓存结果
              catchError(() => of())
            );
          })
        );

        // 合并流:先返回缓存结果,再返回网络请求结果
        return concat(initialCacheResult$, delayedNetworkRequest$);
      })
    );
  };
};

代码细节解释

  1. 缓存读取与初始响应:

    • 通过cache.readQuery直接读取缓存中的查询数据
    • 若缓存存在有效数据,生成包含缓存数据的响应流;否则生成空流,等待网络请求结果
  2. 延迟网络请求处理:

    • 用rxjs的delay(1000)实现1秒延迟,避免立即发起网络请求
    • 通过navigator.onLine检测浏览器在线状态,离线时直接返回空流,不触发网络请求
    • 网络请求成功后,调用cache.writeQuery手动更新缓存,确保后续请求能获取最新数据
  3. 流合并与错误处理:

    • 使用concat合并缓存流和网络流,保证先返回缓存数据,再返回网络更新后的结果
    • 捕获网络请求错误并返回空流,避免错误提示覆盖已展示的缓存内容

使用方式

在创建urql Client时,将这个Exchange添加到exchange数组中(顺序需保证它能拿到cache实例,并能转发请求到fetchExchange):

import { createClient, fetchExchange } from '@urql/core';

const client = createClient({
  url: '/your-graphql-endpoint',
  exchanges: [
    cacheAndNetworkWithDelayExchange,
    fetchExchange,
  ],
});

注意事项

  • 离线检测局限性:navigator.onLine并非100%可靠(比如VPN断开但本地网络仍连通),若需更精准的离线检测,可结合自定义的网络状态监听逻辑
  • 延迟时间调整:可根据业务需求修改delay(1000)中的毫秒数
  • 错误处理扩展:若需要在网络请求失败时提示用户,可在catchError中添加自定义的错误处理逻辑,而非直接返回空流

内容的提问来源于stack exchange,提问作者AHOYAHOY

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 11:03:15