Dart oss fresh
Skill Python51888/StudyDart-Skills/skills/dart-open-source-projects/dart-oss-fresh
Agent skills for Study Dart
npx -y skills add Python51888/StudyDart-Skills --skill dart-oss-freshAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
学习 felangel/fresh 开源项目,通过 OAuth Token 自动刷新库理解 Dart 异步编程模式(Future、拦截器、Stream、网络请求链),掌握实际异步架构设计。
SKILL.md
6.0 KB, as published. Nobody here has run it
学习 Fresh Token 自动刷新库项目
Contents
项目概览
项目名称: fresh 作者: felangel (bloc 作者) GitHub: https://github.com/felangel/fresh Stars: 421 | 难度: 中等
一个纯 Dart 的 OAuth Token 自动刷新库。支持 dio、http 和 graphql 三种 HTTP 客户端。核心设计模式是拦截器——在请求发送前自动检查 Token 是否过期,若过期则自动刷新后再发送原请求。项目中 Future 链式处理、Stream 监听、异步错误恢复等技术是学习 dart-async-concurrency 的绝佳案例。
与 dart-async-concurrency 的关联
该项目集中展示了 dart-async-concurrency 技能的核心内容:
| dart-async-concurrency 主题 | 项目中的应用 |
|---|---|
| Future 链式处理 | then / catchError / whenComplete |
| async/await | Token 刷新方法 |
| Stream 控制 | Token 变化通知流 |
| 异步错误处理 | 401 拦截 → 刷新 → 重试 |
| Future.wait | 批量并发请求 Token 刷新等待 |
| 异步状态管理 | 刷新锁防止重复刷新 |
项目架构分析
核心模型
TokenStorage<T> (abstract)
├── read() → Future<T>
├── write(T) → Future<void>
├── delete() → Future<void>
TokenRefresher<T> (typedef)
└── Future<T> Function(T? token)
Fresh<T> (class)
├── token → Stream<T?>
├── authenticationStatus → Stream<AuthenticationStatus>
├── setToken(T)
├── refreshToken(T?)
└── close()
拦截器机制
class Fresh<T> {
final TokenStorage<T> _tokenStorage;
final TokenRefresher<T> _refreshToken;
final StreamController<T?> _tokenController;
final Stream<T?> get token => _tokenController.stream;
Future<T> _getOrRefreshToken() async {
final current = await _tokenStorage.read();
if (current == null || _isExpired(current)) {
return _refreshToken(current);
}
return current;
}
}
异步编程模式详解
Token 刷新锁模式
防止多个并发请求同时触发刷新:
Future<T?> _refreshLock;
bool _refreshing = false;
Future<T> _validateAndRefresh(T token) async {
if (_isFresh(token)) return token;
if (_refreshing) {
return _refreshLock!; // 等待正在进行的刷新
}
_refreshing = true;
_refreshLock = _refreshToken(token)
.then((newToken) {
_refreshing = false;
return newToken;
})
.catchError((error) {
_refreshing = false;
throw error;
});
return _refreshLock!;
}
Stream 广播模式
enum AuthenticationStatus { authenticated, unauthenticated }
final _authController = StreamController<AuthenticationStatus>.broadcast();
Stream<AuthenticationStatus> get authenticationStatus =>
_authController.stream;
setToken(T token) {
_authController.add(AuthenticationStatus.authenticated);
}
signOut() async {
await _tokenStorage.delete();
_authController.add(AuthenticationStatus.unauthenticated);
}
Workflow: 通过 Fresh 学习 Dart 异步编程
Task Progress
- Step 1: 阅读 Fresh 接口定义。 从抽象类
TokenStorage和typedef TokenRefresher理解架构意图。 - Step 2: 分析拦截器流程。 跟踪一次请求从发出到收到完整响应的 Future 链路。
- Step 3: 理解 Stream 使用。 标注 token Stream 和 authenticationStatus Stream 的生产和消费逻辑。
- Step 4: 运行测试。
dart test观察各个异步场景的测试用例。 - Step 5: 实现自定义 TokenStorage。 基于
dart:io文件系统创建持久化的 TokenStorage。 - Step 6: 编写异步单元测试。 使用
expectLater/emitsInOrder验证 Stream 行为。 - Step 7: 添加超时处理。 为 Token 刷新添加超时和重试逻辑。
- Step 8: Feedback Loop。 运行测试 → 分析异步错误栈 → 修正 → 重复。
条件逻辑
- 如果对 Future 链不理解: 使用
dart-async-concurrency技能中 Future 章节逐一对照。 - 如果需要处理并发刷新: 参考项目的刷新锁模式,避免构造竞态条件。
- 如果 Stream 消费逻辑不对: 检查是单订阅流还是广播流,确保使用正确的 StreamController 类型。
- 如果异步错误被吞没: 在 Future 链中添加
.catchError或使用 try/catch 包裹 await 调用。
Examples
从项目中学到的拦截器模式
Future<Response> _intercept(RequestOptions options) async {
final token = await _getOrRefreshToken();
options.headers['Authorization'] = 'Bearer $token';
return _client.get(options.uri.toString(), headers: options.headers);
}
结合 dart-async-concurrency 的延伸练习:超时重试
Future<T> _refreshWithRetry(T? token, {int retries = 3}) async {
for (var i = 0; i < retries; i++) {
try {
return await _refreshToken(token).timeout(
const Duration(seconds: 10),
);
} on TimeoutException {
if (i == retries - 1) rethrow;
await Future.delayed(Duration(seconds: 1 << i)); // 指数退避
}
}
throw Exception('Token 刷新失败,已达最大重试次数');
}