-
-
Notifications
You must be signed in to change notification settings - Fork 177
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: XStream 返回流同时支持异步迭代 #319
Conversation
Important Review skippedReview was skipped due to path filters ⛔ Files ignored due to path filters (1)
CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including You can disable this status message by setting the 📝 Walkthrough📝 Walkthrough📝 WalkthroughWalkthrough此拉取请求对 Changes
Possibly related PRs
Suggested reviewers
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
components/x-stream/index.ts
(2 hunks)
🔇 Additional comments (3)
components/x-stream/index.ts (3)
140-142
: 添加了 XReadableStream 接口以支持异步迭代
接口 XReadableStream
正确地扩展了 ReadableStream
,并添加了 [Symbol.asyncIterator]()
方法,使流可以直接进行异步迭代。
189-189
: 返回值正确地断言为 XReadableStream
函数返回流,并将其断言为 XReadableStream<Output>
,符合预期。
148-148
: XStream 函数签名更改,请确保兼容性
XStream
函数由 async function*
修改为普通函数,返回类型变为 XReadableStream<Output>
。请确认所有调用 XStream
的地方都已适配新的返回类型,以避免潜在的问题。
请运行以下脚本,检查代码库中对 XStream
的调用,确保调用方已适配新的返回类型:
✅ Verification successful
Let me generate additional verification scripts to check the implementation details of the XStream function.
XStream 函数签名变更已正确适配
根据代码分析,XStream 函数虽然从 async function*
修改为普通函数,但它通过在返回的 stream 对象上显式实现 Symbol.asyncIterator
,保持了与之前相同的异步迭代器接口。所有调用处都使用 for await...of
语法进行迭代,完全兼容新的实现方式。
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# 描述:查找所有对 XStream 的调用,并显示其上下文
rg -A 5 "XStream\("
Length of output: 5018
Script:
#!/bin/bash
# Check the implementation of XStream function and its return type
ast-grep --pattern 'function XStream<$_>($_) {
$$$
}'
# Check if there are any type definitions related to XStream
rg "type.*XStream" -A 2
Length of output: 3644
components/x-stream/index.ts
Outdated
/** support async iterator */ | ||
(stream as XReadableStream<Output>)[Symbol.asyncIterator] = async function* () { | ||
const reader = this.getReader(); | ||
try { | ||
while (true) { | ||
const { done, value } = await reader.read(); | ||
if (done) { | ||
return; | ||
} | ||
yield value; | ||
} | ||
} finally { | ||
reader.cancel(); | ||
} | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
在 finally 块中应使用 reader.releaseLock()
而非 reader.cancel()
在异步迭代器的 finally
块中,调用 reader.cancel()
会取消流的读取,并可能导致未读取的数据被丢弃。如果只是想释放读取器的锁,应使用 reader.releaseLock()
。
请应用以下修改来修正此问题:
} finally {
- reader.cancel();
+ reader.releaseLock();
}
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
/** support async iterator */ | |
(stream as XReadableStream<Output>)[Symbol.asyncIterator] = async function* () { | |
const reader = this.getReader(); | |
try { | |
while (true) { | |
const { done, value } = await reader.read(); | |
if (done) { | |
return; | |
} | |
yield value; | |
} | |
} finally { | |
reader.cancel(); | |
} | |
}; | |
/** support async iterator */ | |
(stream as XReadableStream<Output>)[Symbol.asyncIterator] = async function* () { | |
const reader = this.getReader(); | |
try { | |
while (true) { | |
const { done, value } = await reader.read(); | |
if (done) { | |
return; | |
} | |
yield value; | |
} | |
} finally { | |
reader.releaseLock(); | |
} | |
}; |
感谢贡献! |
Bundle ReportChanges will decrease total bundle size by 74 bytes (-0.05%) ⬇️. This is within the configured threshold ✅ Detailed changes
|
这个 PR 的实现思路让我学习到了,优雅! |
@ppbl |
异常捕获的问题,加了 finally 块,splitPart 里面的错误就捕获不到直接抛出来了 |
现在的测试错误是 目前改成 AsyncIterator 对象的形式可以解决 (stream as XReadableStream<Output>)[Symbol.asyncIterator] = function () {
const reader = this.getReader();
return {
async next() {
const { done, value } = await reader.read();
return {
done,
value: value!,
};
},
async return() {
await reader.cancel();
return {
done: true,
value: null,
};
},
};
}; 这样的话带来的问题是 不过我个人感觉这个也可以在外部自行处理 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (2)
components/x-stream/index.ts (2)
146-146
: 函数签名修改合理,建议更新文档移除
async
关键字的修改符合新的实现方式。建议在函数文档中补充说明返回的流对象同时支持标准流操作和异步迭代的特性。
171-192
: 建议添加性能优化考虑添加缓冲机制以提高性能,特别是在处理大量数据时。
(stream as XReadableStream<Output>)[Symbol.asyncIterator] = function () { const reader = this.getReader(); + const buffer: Output[] = []; + const bufferSize = 1024; // 可配置的缓冲区大小 return { async next() { try { + if (buffer.length > 0) { + return { + done: false, + value: buffer.shift()!, + }; + } const { done, value } = await reader.read(); + if (value && Array.isArray(value)) { + buffer.push(...value.slice(1)); + return { + done: false, + value: value[0], + }; + } return { done, value: value ?? null, }; } catch (error) { reader.releaseLock(); throw error; } }, // ... 其他方法保持不变 }; };
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (2)
components/x-stream/__tests__/index.test.tsx
(1 hunks)components/x-stream/index.ts
(2 hunks)
🔇 Additional comments (3)
components/x-stream/__tests__/index.test.tsx (1)
66-68
: 需要验证空值处理的必要性
这个改动表明新的流实现可能会产生空值。虽然过滤掉空值可以让测试通过,但这可能掩盖了底层实现的问题。
建议:
- 确认空值是否是预期行为
- 如果是预期的,请在代码注释中说明原因
- 如果不是预期的,建议在流的实现中处理,而不是在消费端过滤
✅ Verification successful
空值处理是合理的实现
根据代码分析,空值检查是合理的:
- 这个空值检查是在使用自定义
transformStream
的测试用例中,而不是默认的 SSE 处理流程 - 从实现来看,
XStream
支持可选的自定义transformStream
,允许用户定义自己的数据转换逻辑 - 自定义转换流可能产生空值是完全合理的,这取决于具体的业务需求
建议:
- 在测试用例中添加注释,说明空值检查的目的是为了支持自定义转换流的灵活性
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# 检查其他测试文件中是否有类似的空值处理模式
rg -l 'if.*value.*push' --type ts
Length of output: 35
Script:
#!/bin/bash
# 搜索流实现中的相关代码
ast-grep --pattern 'class $_ implements Stream {
$$$
}'
# 查找测试文件中的相关代码模式
rg -A 5 -B 5 'result.push.*value' --type ts
# 查找可能的空值处理相关代码
rg -A 3 'null.*value|undefined.*value' --type ts
Length of output: 4089
Script:
#!/bin/bash
# 检查自定义转换流的实现
ast-grep --pattern 'const customTransform = $$$'
# 查找流实现中的相关代码
rg -A 5 'class.*Stream.*{' --type ts
# 检查转换流的实现
rg -A 10 'transform.*Stream' --type ts
Length of output: 8571
components/x-stream/index.ts (2)
140-141
: 类型定义清晰且符合预期
XReadableStream
类型很好地结合了 ReadableStream
和 AsyncIterable
的特性,使流既支持标准的流操作,又支持异步迭代。
171-192
:
异步迭代器实现需要改进
当前实现存在以下问题:
- 在
return()
方法中使用cancel()
可能导致未读取的数据丢失 - 缺少错误处理机制
- 没有适当的资源清理
建议应用以下修改:
(stream as XReadableStream<Output>)[Symbol.asyncIterator] = function () {
const reader = this.getReader();
return {
async next() {
+ try {
const { done, value } = await reader.read();
return {
done,
value: value!,
};
+ } catch (error) {
+ reader.releaseLock();
+ throw error;
+ }
},
async return() {
- await reader.cancel();
+ reader.releaseLock();
return {
done: true,
value: null,
};
},
+ async throw(error) {
+ reader.releaseLock();
+ throw error;
+ },
};
};
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (3)
components/x-stream/index.ts (3)
140-141
: 建议改进类型定义的类型安全性建议避免使用
any
类型,可以使用更具体的类型或泛型约束来提高类型安全性。-type XReadableStream<R = any> = ReadableStream<R> & AsyncIterable<R>; +type XReadableStream<R = unknown> = ReadableStream<R> & AsyncIterable<R>;
Line range hint
146-170
: 建议增强错误处理和错误信息当前的错误处理可以更加完善:
- 错误信息应该包含实际接收到的类型信息
- 建议添加对 transformStream 的类型验证
if (!(readableStream instanceof ReadableStream)) { - throw new Error('The options.readableStream must be an instance of ReadableStream.'); + throw new Error( + `options.readableStream 必须是 ReadableStream 的实例,但收到了 ${Object.prototype.toString.call(readableStream)}` + ); } + if (transformStream && !(transformStream instanceof TransformStream)) { + throw new Error( + `options.transformStream 必须是 TransformStream 的实例,但收到了 ${Object.prototype.toString.call(transformStream)}` + ); + }
Line range hint
146-192
: 建议考虑添加流的取消信号支持目前的实现虽然支持了异步迭代,但在实际应用中可能需要支持更细粒度的取消控制。建议考虑添加对
AbortSignal
的支持。建议在
XStreamOptions
接口中添加可选的signal
参数:export interface XStreamOptions<Output> { readableStream: ReadableStream<Uint8Array>; transformStream?: TransformStream<string, Output>; signal?: AbortSignal; }这样可以让上层应用更灵活地控制流的生命周期,特别是在需要提前取消长时间运行的流处理时。
components/x-stream/index.ts
Outdated
/** support async iterator */ | ||
(stream as XReadableStream<Output>)[Symbol.asyncIterator] = function () { | ||
const reader = this.getReader(); | ||
return { | ||
async next() { | ||
const { done, value } = await reader.read(); | ||
return { | ||
done, | ||
value: value ?? null, | ||
}; | ||
}, | ||
async return() { | ||
await reader.cancel(); | ||
return { | ||
done: true, | ||
value: null, | ||
}; | ||
}, | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
建议优化资源清理逻辑
当前的异步迭代器实现存在以下问题:
- 没有正确处理读取器的锁定释放
- 在错误发生时可能会导致资源泄漏
建议按照以下方式修改实现:
(stream as XReadableStream<Output>)[Symbol.asyncIterator] = function () {
const reader = this.getReader();
return {
async next() {
- const { done, value } = await reader.read();
- return {
- done,
- value: value ?? null,
- };
+ try {
+ const { done, value } = await reader.read();
+ return {
+ done,
+ value: value ?? null,
+ };
+ } catch (error) {
+ reader.releaseLock();
+ throw error;
+ }
},
async return() {
- await reader.cancel();
+ try {
+ await reader.cancel();
+ } finally {
+ reader.releaseLock();
+ }
return {
done: true,
value: null,
};
},
};
};
这样的修改可以:
- 确保在发生错误时释放读取器锁
- 保证在迭代器结束时正确清理资源
- 防止资源泄漏
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
/** support async iterator */ | |
(stream as XReadableStream<Output>)[Symbol.asyncIterator] = function () { | |
const reader = this.getReader(); | |
return { | |
async next() { | |
const { done, value } = await reader.read(); | |
return { | |
done, | |
value: value ?? null, | |
}; | |
}, | |
async return() { | |
await reader.cancel(); | |
return { | |
done: true, | |
value: null, | |
}; | |
}, | |
}; | |
}; | |
/** support async iterator */ | |
(stream as XReadableStream<Output>)[Symbol.asyncIterator] = function () { | |
const reader = this.getReader(); | |
return { | |
async next() { | |
try { | |
const { done, value } = await reader.read(); | |
return { | |
done, | |
value: value ?? null, | |
}; | |
} catch (error) { | |
reader.releaseLock(); | |
throw error; | |
} | |
}, | |
async return() { | |
try { | |
await reader.cancel(); | |
} finally { | |
reader.releaseLock(); | |
} | |
return { | |
done: true, | |
value: null, | |
}; | |
}, | |
}; | |
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@YumoImer,您好。即使在非网络请求的场景下,确保正确释放读取器的锁定仍然是重要的。未释放的读取器可能会导致资源泄漏,特别是在长时间运行的应用程序中。通过在发生错误或迭代器结束时释放锁定,可以确保资源被及时清理,避免潜在的问题。
因此,我建议在实现中加入对读取器锁定的释放逻辑,以提高代码的健壮性和可靠性。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这里的核心问题是:需不需要替用户将流资源释放?
我认为在网络请求的场景,这样做没问题。
其他场景你有相关经验吗?或者,这块你如何考虑的?@ppbl
你是指的 reader.cancel() 方法吗,我刚才去看了下 web-streams-polyfill 的实现,有一个 preventCancel 选项,我们也可以参考添加一个,如果用户传 true 则可以只调用 reader.releaseLock();
const stream = XStream({
readableStream: response.body,
preventCancel: true
})
这样的话,其实现在的实现完全是可以向前兼容的,可以等需要的时候随时支持类似 preventCancel 以及更多的选项~
你看看呢~ @YumoImer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我的观点是倾向于尽量向标准看齐,也可以暴露一个 values 方法可以显式的传参数,直接用 for..await..of 迭代的时候就用默认的参数
👍 这个点我也认同~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
嗯那现在是要同步支持 values 方法吗,需要的话我加一下试试
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
我明白你的意思了,那异步迭代就先保持目前的逻辑吧,取消的可以后续再说~
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
可以的~取消这个行为还是辛苦你来如何 😄 另外写个 RFC 我们充分讨论下
[RFC] XStream support ... options
这里我最初设计的时候认为「空串不应该触发迭代」所以这里是个 break change |
明天我再想想怎么处理好些。先睡了~ |
测试恢复了, reader.cancel() 前面加个 await 完美解决~ |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #319 +/- ##
==========================================
- Coverage 91.48% 91.38% -0.10%
==========================================
Files 66 66
Lines 1468 1451 -17
Branches 395 383 -12
==========================================
- Hits 1343 1326 -17
Misses 125 125 ☔ View full report in Codecov by Sentry. |
评论了一波,辛苦看下~感谢~ @ppbl |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
components/x-stream/index.ts (2)
140-141
: 类型定义清晰,建议添加文档注释类型定义正确地结合了
ReadableStream
和AsyncGenerator
,符合设计目标。建议添加 JSDoc 文档注释来说明这个类型的用途:
+/** + * @description 支持异步迭代的可读流类型 + * @template R - 流中的数据类型,默认为 SSEOutput + */ type XReadableStream<R = SSEOutput> = ReadableStream<R> & AsyncGenerator<R>;
156-169
: 建议增强类型断言的安全性当前的类型转换实现可以更加类型安全。
建议添加类型守卫函数:
function isXReadableStream<T>(stream: ReadableStream<T>): stream is XReadableStream<T> { return stream instanceof ReadableStream; }然后修改类型断言:
- ) as XReadableStream<Output>; + ) as ReturnType<typeof XStream<Output>>;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
components/x-stream/index.ts
(2 hunks)
🔇 Additional comments (2)
components/x-stream/index.ts (2)
146-146
: 函数签名修改合理
移除 async
关键字并返回 XReadableStream
的修改符合新的类型系统设计,同时保持了向后兼容性。
171-184
: 🛠️ Refactor suggestion
建议增强错误处理机制
当前的异步迭代器实现缺少错误处理机制。根据之前的讨论,虽然不自动处理资源清理,但仍建议添加基本的错误处理。
建议修改实现:
stream[Symbol.asyncIterator] = async function* () {
const reader = this.getReader();
+ try {
while (true) {
const { done, value } = await reader.read();
if (done) break;
if (!value) continue;
yield value;
}
+ } catch (error) {
+ // 让错误继续向上传播,用户可以在自己的 try-catch 中处理清理逻辑
+ throw error;
+ }
};
Likely invalid or redundant comment.
我这边没问题了~ @ant-design/ant-design-x 大家帮看一下 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
感谢贡献!
加个用例? |
测试用例还是 demo 用例?pr 的主要改动是把 至于直接的目的是为了支持取消流,这个用例可能是一个组合用例,后面看看怎么加一下?😊 |
我想了下,加一个「支持取消流 demo 用例」& 加一个「返回 stream 的测试用例」。 对于开发者用户:方便通过文档了解 XStream 返回的 stream 如何使用。 对于 Contributors 们,未来对于 XStream 的返回变动也有用例保障。 然后这两个用例都是和本 PR 变化相关的,就在这个 PR 内完成吧。辛苦 @ppbl 看下~ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
components/x-stream/__tests__/index.test.tsx (1)
101-112
: 测试用例实现正确且符合需求测试用例验证了 XStream 返回 ReadableStream 实例的新功能,这与 PR 的目标保持一致。测试数据的选择也很合适,包含了事件和数据字段。
不过建议添加以下测试场景:
- 验证返回的流是否正确支持取消操作
- 验证在取消后是否正确清理了资源
建议添加如下测试用例:
it('should support cancellation', async () => { const stream = XStream({ readableStream: new ReadableStream({ async start(controller) { // 模拟长响应 for (let i = 0; i < 1000; i++) { controller.enqueue(new TextEncoder().encode(`event: message\ndata: ${i}\n\n`)); await new Promise(resolve => setTimeout(resolve, 10)); } controller.close(); }, }), }); const reader = stream.getReader(); // 读取几个值后取消 await reader.read(); await reader.read(); await reader.cancel(); // 验证取消后的状态 expect(reader.closed).resolves.toBeUndefined(); });
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (3)
components/useXChat/demo/stream-cancel.tsx (3)
58-58
: 建议优化空对象参数模式当前的空对象解构模式可能会引起困惑。建议明确声明预期的参数类型。
-request: async ({}, { onSuccess, onUpdate }) => { +request: async ({ message }, { onSuccess, onUpdate }) => {🧰 Tools
🪛 Biome (1.9.4)
[error] 58-58: Unexpected empty object pattern.
(lint/correctness/noEmptyPattern)
76-78
: 建议增加类型安全性解析 JSON 数据时缺少类型检查。建议添加类型定义和验证。
+interface StreamData { + id: string; + content: string; +} + +function isValidStreamData(data: unknown): data is StreamData { + return ( + typeof data === 'object' && + data !== null && + 'id' in data && + 'content' in data && + typeof (data as StreamData).content === 'string' + ); +} - const data = JSON.parse(value.data); + const data = JSON.parse(value.data); + if (!isValidStreamData(data)) { + throw new Error('Invalid stream data format'); + } current += data.content || '';
32-49
: 建议优化模拟数据生成逻辑当前的模拟数据生成方式可以更加灵活。建议将延迟时间和内容块作为可配置参数。
-function mockReadableStream() { +function mockReadableStream({ + delay = 300, + chunks = contentChunks, +}: { + delay?: number; + chunks?: string[]; +} = {}) { const sseChunks: string[] = []; - for (let i = 0; i < contentChunks.length; i++) { + for (let i = 0; i < chunks.length; i++) { const sseEventPart = - `event: message\ndata: {"id":"${i}","content":"${contentChunks[i]}"}\n\n`; + `event: message\ndata: {"id":"${i}","content":"${chunks[i]}"}\n\n`; sseChunks.push(sseEventPart); } return new ReadableStream({ async start(controller) { for (const chunk of sseChunks) { - await new Promise((resolve) => setTimeout(resolve, 300)); + await new Promise((resolve) => setTimeout(resolve, delay)); controller.enqueue(new TextEncoder().encode(chunk)); } controller.close(); }, }); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
components/useXChat/demo/stream-cancel.md
(1 hunks)components/useXChat/demo/stream-cancel.tsx
(1 hunks)components/useXChat/index.en-US.md
(1 hunks)components/useXChat/index.zh-CN.md
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- components/useXChat/demo/stream-cancel.md
🧰 Additional context used
🪛 LanguageTool
components/useXChat/index.zh-CN.md
[uncategorized] ~24-~24: A punctuation mark might be missing here.
Context: ...c="./demo/stream-cancel.tsx">打断输出 多项建议<...
(AI_EN_LECTOR_MISSING_PUNCTUATION)
🪛 Biome (1.9.4)
components/useXChat/demo/stream-cancel.tsx
[error] 58-58: Unexpected empty object pattern.
(lint/correctness/noEmptyPattern)
🔇 Additional comments (2)
components/useXChat/index.zh-CN.md (1)
24-24
: 文档更新正确且完整
新增的示例代码条目与现有格式保持一致,清晰地展示了流式输出中断的功能。
🧰 Tools
🪛 LanguageTool
[uncategorized] ~24-~24: A punctuation mark might be missing here.
Context: ...c="./demo/stream-cancel.tsx">打断输出多项建议<...
(AI_EN_LECTOR_MISSING_PUNCTUATION)
components/useXChat/index.en-US.md (1)
23-23
: 英文文档更新与中文文档保持一致
新增的示例代码条目正确展示了流式输出中断功能,保持了文档的双语一致性。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
components/useXChat/demo/stream-cancel.tsx (1)
75-85
:⚠️ Potential issue建议增加 try/catch 来完善错误处理
若在读取流时发生异常,目前的 while 循环会直接中断。可以在循环外增加 try/catch 以捕获并处理异常,提升健壮性并使用户可感知错误。
🧹 Nitpick comments (1)
components/useXChat/demo/stream-cancel.tsx (1)
64-64
: 避免使用空对象模式来解构未使用的参数static analysis 工具提示此处的空对象模式不符合规范。若首个参数确实无需使用,建议使用命名占位符或下划线代替,以提高可读性并避免误解:
- request: async ({}, { onSuccess, onUpdate }) => { + request: async (_unused, { onSuccess, onUpdate }) => {🧰 Tools
🪛 Biome (1.9.4)
[error] 64-64: Unexpected empty object pattern.
(lint/correctness/noEmptyPattern)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
components/useXChat/demo/stream-cancel.tsx
(1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
components/useXChat/demo/stream-cancel.tsx
[error] 64-64: Unexpected empty object pattern.
(lint/correctness/noEmptyPattern)
@ppbl lint 挂了,辛苦看下~ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
components/useXChat/demo/stream-cancel.tsx (2)
17-30
: 建议增强模拟数据的类型定义和文档说明为了提高代码的可维护性和可读性,建议:
- 为
contentChunks
添加类型定义- 添加注释说明数据的用途和格式
+// 模拟的消息内容片段,用于演示流式响应 +const contentChunks: readonly string[] = [ 'He', 'llo', // ... 其他内容 ];
113-113
: 建议优化取消操作的加载状态处理当前的取消操作没有考虑加载状态的处理,建议在取消操作时更新相关状态。
- onCancel={() => abortRef.current()} + onCancel={() => { + abortRef.current(); + // 可以考虑添加取消状态的处理 + agent.setRequesting(false); + }}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
components/useXChat/demo/stream-cancel.tsx
(1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
components/useXChat/demo/stream-cancel.tsx
[error] 64-64: Unexpected empty object pattern.
(lint/correctness/noEmptyPattern)
🔇 Additional comments (1)
components/useXChat/demo/stream-cancel.tsx (1)
75-85
: 建议增强错误处理机制
当前的流读取逻辑缺少错误处理,可能导致运行时异常。
- while (reader) {
+ try {
+ while (reader) {
const { value, done } = await reader.read();
if (done) {
onSuccess(current);
break;
}
if (!value) continue;
const data = JSON.parse(value.data);
current += data.content || '';
onUpdate(current);
+ }
+ } catch (error) {
+ console.error('Stream reading error:', error);
+ onSuccess(current);
+ }
@YumoImer 修复了~ 不好意思 |
@ppbl 需要更新下这个 demo 的测试用例快照~ ● renders components/useXChat/demo/stream-cancel.tsx correctly |
先合并了, 覆盖率下降是因为整体代码行数提高。 |
why?
目前 XStream 返回一个 AsyncGenerator,支持 for await of 异步迭代,
但是实际开发中遇到响应很长中途中断是很常见的需求,此时通过 XStream 无法取消。
如果返回一个 stream 则可以自定义实现取消逻辑,同时通过给 stream 增加.asyncIterator 属性仍然可以支持异步迭代
(想过 polyfill, 一是编码的时候遇到类型问题不想断言,然后觉得毕竟影响比较大,现在这种方式只影响使用 XStream 的 stream)
Summary by CodeRabbit
XReadableStream
类型,支持异步迭代功能。XStream
函数的签名,支持使用for await...of
语法进行流的异步迭代。XStream
函数返回ReadableStream
实例的功能。stream-cancel
功能添加了简体中文和英文的文档说明。useXChat
文档中新增了中断输出的示例代码。