在这里插入图片描述
在这里插入图片描述

一、异步状态管理的挑战

1.1 异步操作的复杂性

在移动应用开发中,异步操作是不可避免的。常见的异步场景包括:

  • 网络请求
  • 数据库操作
  • 文件读写
  • 定时器任务

这些操作带来了几个核心挑战:

挑战 描述 影响
状态不一致 异步操作期间状态可能被多次修改 数据错乱
竞态条件 多个异步操作相互干扰 难以调试的bug
加载状态 需要管理loading、success、error三种状态 代码复杂度增加
取消操作 用户可能在操作完成前取消 资源浪费

1.2 传统异步处理方式

// 传统回调方式
fetchData((data) => {
  this.data = data;
}, (error) => {
  this.error = error;
});

// Promise方式
fetchData().then(data => {
  this.data = data;
}).catch(error => {
  this.error = error;
});

1.3 HarmonyOS V2的解决方案

V2状态管理提供了更好的异步处理机制:

@Entry
@ComponentV2
struct AsyncDemo {
  @Local loading: boolean = false;
  @Local data: User[] = [];
  @Local error: string = '';

  async fetchData(): Promise<void> {
    this.loading = true;
    this.error = '';
    
    try {
      const response = await http.get('https://api.example.com/users');
      this.data = response.data;
    } catch (err) {
      this.error = '加载失败';
    } finally {
      this.loading = false;
    }
  }
}

二、异步状态的三种状态模式

2.1 状态模式设计

异步操作通常需要管理三种状态:

interface AsyncState<T> {
  loading: boolean;
  data: T | null;
  error: string;
}

状态转换图:

          ┌──────────────┐
          │   Initial    │
          │ (loading=false│
          │  data=null)  │
          └──────┬───────┘
                 │ fetch()
                 ▼
          ┌──────────────┐
          │   Loading    │
          │ (loading=true│
          │  data=null)  │
          └──────┬───────┘
                 │
        ┌────────┴────────┐
        │                 │
        ▼                 ▼
┌──────────────┐   ┌──────────────┐
│   Success    │   │    Error     │
│ (loading=    │   │ (loading=    │
│  false,      │   │  false,      │
│  data=[])    │   │  error=msg)  │
└──────────────┘   └──────────────┘

2.2 实现异步数据Store

@ObservedV2
class AsyncDataStore {
  @Trace state: AsyncState<User[]> = {
    loading: false,
    data: [],
    error: ''
  };

  @Trace refreshCount: number = 0;

  async fetchUsers(): Promise<void> {
    this.state.loading = true;
    this.state.error = '';

    try {
      await this.delay(1500);
      
      const mockUsers: User[] = [
        { id: 1, name: '张三', email: 'zhangsan@example.com' },
        { id: 2, name: '李四', email: 'lisi@example.com' },
        { id: 3, name: '王五', email: 'wangwu@example.com' }
      ];

      this.state.data = mockUsers;
      this.refreshCount++;
    } catch (err) {
      this.state.error = '加载失败,请重试';
    } finally {
      this.state.loading = false;
    }
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

export const asyncDataStore = new AsyncDataStore();

2.3 异步状态管理的关键点

1. 状态重置

this.state.loading = true;
this.state.error = '';

2. 错误处理

try {
  // 异步操作
} catch (err) {
  this.state.error = '加载失败';
}

3. 最终状态

finally {
  this.state.loading = false;
}

三、异步加载组件实现

3.1 完整的异步加载页面

@Entry
@ComponentV2
struct AsyncDataPage {
  @Local searchQuery: string = '';
  private store = asyncDataStore;

  aboutToAppear(): void {
    this.store.fetchUsers();
  }

  build() {
    Column({ space: 16 }) {
      // 搜索框
      TextInput({ placeholder: '搜索用户' })
        .width('100%')
        .height(44)
        .backgroundColor('#FFFFFF')
        .borderRadius(8)
        .onChange((value: string) => {
          this.searchQuery = value;
        })

      // 加载状态
      if (this.store.state.loading) {
        this.buildLoadingState();
      } else if (this.store.state.error) {
        this.buildErrorState();
      } else if (this.store.state.data.length === 0) {
        this.buildEmptyState();
      } else {
        this.buildDataList();
      }
    }
    .width('100%')
    .padding(20)
    .backgroundColor('#F5F5F7')
  }

  @Builder
  buildLoadingState(): void {
    Column({ space: 12 }) {
      LoadingProgress()
        .width(48)
        .height(48)
        .color('#007DFF')
      Text('加载中...')
        .fontSize(14)
        .fontColor('#666666')
    }
    .width('100%')
    .height(300)
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  buildErrorState(): void {
    Column({ space: 12 }) {
      Text('❌')
        .fontSize(48)
      Text(this.store.state.error)
        .fontSize(14)
        .fontColor('#FF4757')
      Button('重新加载')
        .width(120)
        .height(40)
        .backgroundColor('#007DFF')
        .onClick(() => this.store.fetchUsers())
    }
    .width('100%')
    .height(300)
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  buildEmptyState(): void {
    Column({ space: 12 }) {
      Text('📭')
        .fontSize(48)
      Text('暂无数据')
        .fontSize(14)
        .fontColor('#999999')
    }
    .width('100%')
    .height(300)
    .justifyContent(FlexAlign.Center)
  }

  @Builder
  buildDataList(): void {
    List({ space: 12 }) {
      ForEach(this.filteredUsers, (user: User) => {
        ListItem() {
          Row({ space: 12 }) {
            Text(user.avatar || '👤')
              .fontSize(40)
            Column({ space: 4 }) {
              Text(user.name)
                .fontSize(16)
                .fontWeight(FontWeight.Medium)
              Text(user.email)
                .fontSize(12)
                .fontColor('#666666')
            }
          }
          .width('100%')
          .padding(16)
          .backgroundColor('#FFFFFF')
          .borderRadius(12)
        }
      })
    }
    .width('100%')
    .layoutWeight(1)
  }

  get filteredUsers(): User[] {
    if (!this.searchQuery.trim()) {
      return this.store.state.data;
    }
    const keyword = this.searchQuery.toLowerCase();
    return this.store.state.data.filter(user =>
      user.name.toLowerCase().includes(keyword) ||
      user.email.toLowerCase().includes(keyword)
    );
  }
}

3.2 状态同步机制

当Store状态变化时,UI会自动更新:

// Store中的状态变化
this.state.data = mockUsers;

// 触发UI更新
// UI组件自动重新渲染

3.3 生命周期管理

aboutToAppear(): void {
  this.store.fetchUsers();
}

aboutToDisappear(): void {
  // 可以在这里取消未完成的请求
}

四、高级异步模式

4.1 请求取消机制

@ObservedV2
class AsyncDataStore {
  private abortController: AbortController | null = null;

  async fetchUsers(): Promise<void> {
    // 取消之前的请求
    if (this.abortController) {
      this.abortController.abort();
    }

    this.abortController = new AbortController();
    this.state.loading = true;

    try {
      const response = await fetch('https://api.example.com/users', {
        signal: this.abortController.signal
      });
      this.state.data = await response.json();
    } catch (err) {
      if (err.name !== 'AbortError') {
        this.state.error = '加载失败';
      }
    } finally {
      this.state.loading = false;
      this.abortController = null;
    }
  }
}

4.2 请求防抖

class DebounceStore {
  private debounceTimer: number | null = null;

  fetchWithDebounce(query: string): void {
    if (this.debounceTimer) {
      clearTimeout(this.debounceTimer);
    }

    this.debounceTimer = setTimeout(() => {
      this.fetchData(query);
      this.debounceTimer = null;
    }, 300);
  }

  private async fetchData(query: string): Promise<void> {
    // 实际的请求逻辑
  }
}

4.3 请求缓存

@ObservedV2
class CachedDataStore {
  private cache: Map<string, CacheEntry> = new Map();

  async fetchData(key: string): Promise<Data> {
    // 检查缓存
    const cached = this.cache.get(key);
    if (cached && !this.isExpired(cached.timestamp)) {
      return cached.data;
    }

    // 发起请求
    const data = await this.fetchFromAPI(key);
    
    // 更新缓存
    this.cache.set(key, {
      data,
      timestamp: Date.now()
    });

    return data;
  }

  private isExpired(timestamp: number): boolean {
    return Date.now() - timestamp > 5 * 60 * 1000; // 5分钟过期
  }
}

五、响应式数据流架构

5.1 数据流模式

用户操作 → Action → Dispatcher → Store → State → UI
                                              ↑     │
                                              └─────┘

5.2 单向数据流

// Action定义
interface Action {
  type: string;
  payload?: any;
}

// Dispatcher
class Dispatcher {
  private store: Store;

  dispatch(action: Action): void {
    switch (action.type) {
      case 'FETCH_START':
        this.store.setLoading(true);
        break;
      case 'FETCH_SUCCESS':
        this.store.setData(action.payload);
        break;
      case 'FETCH_ERROR':
        this.store.setError(action.payload);
        break;
    }
  }
}

5.3 状态订阅模式

class Store {
  private subscribers: Set<() => void> = new Set();

  subscribe(callback: () => void): () => void {
    this.subscribers.add(callback);
    return () => this.subscribers.delete(callback);
  }

  protected notify(): void {
    this.subscribers.forEach(callback => callback());
  }
}

六、错误处理策略

6.1 错误分类

错误类型 处理策略 用户提示
网络错误 重试机制 检查网络连接
超时错误 增加超时时间 请求超时,请重试
服务器错误 记录日志 服务器繁忙,请稍后再试
数据格式错误 数据校验 数据格式错误
权限错误 引导登录 请登录后重试

6.2 重试机制

async fetchWithRetry(url: string, retries: number = 3): Promise<Response> {
  for (let i = 0; i < retries; i++) {
    try {
      const response = await fetch(url);
      if (!response.ok) throw new Error('HTTP error');
      return response;
    } catch (err) {
      if (i === retries - 1) throw err;
      await this.delay(Math.pow(2, i) * 1000); // 指数退避
    }
  }
  throw new Error('Max retries exceeded');
}

6.3 用户友好的错误提示

@Builder
buildErrorState(): void {
  Column({ space: 16 }) {
    this.buildErrorIcon(this.store.state.error);
    Text(this.store.state.error)
      .fontSize(16)
      .fontColor('#FF4757')
    Row({ space: 12 }) {
      Button('重试')
        .width(100)
        .height(40)
        .backgroundColor('#007DFF')
        .onClick(() => this.store.fetchUsers())
      Button('反馈问题')
        .width(100)
        .height(40)
        .backgroundColor('#F0F0F2')
        .fontColor('#666666')
        .onClick(() => this.showFeedback())
    }
  }
}

七、性能优化

7.1 请求合并

class BatchRequestStore {
  private pendingRequests: Map<string, Promise<any>> = new Map();

  async fetch(key: string): Promise<any> {
    // 如果已有相同请求在进行,复用它
    if (this.pendingRequests.has(key)) {
      return this.pendingRequests.get(key);
    }

    const promise = this.doFetch(key).finally(() => {
      this.pendingRequests.delete(key);
    });

    this.pendingRequests.set(key, promise);
    return promise;
  }
}

7.2 懒加载

@Entry
@ComponentV2
struct LazyLoadPage {
  @Local visibleItems: Set<number> = new Set();

  build() {
    List({ space: 12 }) {
      ForEach(items, (item, index) => {
        ListItem() {
          ItemComponent({ data: item, load: this.visibleItems.has(index) })
        }
        .onAppear(() => {
          this.visibleItems.add(index);
        })
        .onDisappear(() => {
          this.visibleItems.delete(index);
        })
      })
    }
  }
}

7.3 虚拟列表

List({ space: 12, scroller: this.scroller }) {
  LazyForEach(this.dataSource, (item: ListItem) => {
    ListItem() {
      ItemCard({ item })
    }
    .height(100)
  })
}
.height(500)

八、测试异步状态

8.1 单元测试

describe('AsyncDataStore', () => {
  it('should fetch users successfully', async () => {
    const store = new AsyncDataStore();
    
    await store.fetchUsers();
    
    expect(store.state.loading).toBe(false);
    expect(store.state.error).toBe('');
    expect(store.state.data.length).toBeGreaterThan(0);
  });

  it('should handle errors', async () => {
    const store = new AsyncDataStore();
    // 模拟网络错误
    jest.spyOn(global, 'fetch').mockRejectedValue(new Error('Network error'));
    
    await store.fetchUsers();
    
    expect(store.state.loading).toBe(false);
    expect(store.state.error).toBe('加载失败,请重试');
  });
});

8.2 集成测试

it('should display loading state', async () => {
  const page = new AsyncDataPage();
  page.build();
  
  expect(page.findComponent(LoadingProgress)).toBeDefined();
});

it('should display data after fetch', async () => {
  const page = new AsyncDataPage();
  page.build();
  
  await waitFor(() => {
    return page.findComponent(List).children.length > 0;
  }, { timeout: 2000 });
});

九、最佳实践总结

9.1 状态管理原则

  1. 状态集中管理:将相关状态放在一起
  2. 状态不可变:使用不可变更新模式
  3. 状态可见性:明确状态的作用域
  4. 状态验证:更新前验证状态合法性

9.2 异步操作原则

  1. 始终处理错误:不要忽略catch块
  2. 管理加载状态:避免用户困惑
  3. 取消不必要的请求:节省资源
  4. 添加重试机制:提高可靠性

9.3 代码组织建议

src/
├── stores/
│   ├── AsyncDataStore.ts
│   └── types.ts
├── pages/
│   └── AsyncDataPage.ts
└── utils/
    └── http.ts

参考资料

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐