单元测试中使用asyncIterator始终无法解析值的问题排查
问题分析与修复
你的测试代码存在几个关键问题,导致迭代无法正常推进并完成:
核心问题
每次
read都创建新的异步迭代器
在getReader的read方法里,每次调用都会执行mockReadable[Symbol.asyncIterator]()生成新的迭代器实例,这意味着每次读取都会从头开始,永远拿到第一个yield的值,无法推进到后续内容。无限循环未处理结束状态
你的while(true)循环没有在done为true时退出,导致测试会无限执行,最终Jest会超时失败。异步迭代器错误使用
yield null
标准的异步迭代器不需要通过yield null来标记结束,当迭代器执行完毕时,next()会自动返回{ done: true, value: undefined }。外部计数变量与迭代器状态脱节
使用外部变量i来控制结束状态是不可靠的,应该直接依赖迭代器自身的done状态。
修复后的代码
var util = require("util"); describe("stream", () => { it("should work", async () => { const completionStream = { toReadableStream: () => { // 预先创建异步迭代器实例,避免每次read都新建 const iterator = (async function* () { const encoder = new util.TextEncoder("utf-8"); yield encoder.encode( JSON.stringify({ id: "1", object: "text", created: 1629159835, model: "gpt4", choices: [ { index: 0, delta: { content: "Hello" }, finish_reason: null, }, ], }) ); yield encoder.encode( JSON.stringify({ id: "1", object: "text", created: 1629159835, model: "gpt4", choices: [ { index: 0, delta: { content: "there" }, finish_reason: null, }, ], }) ); // 不需要yield null,迭代器自然结束即可 })(); const mockReadable = { [Symbol.asyncIterator]: () => iterator, getReader: () => { return { read: async () => { // 复用同一个迭代器实例 const result = await iterator.next(); if (result.done) { return { done: true, value: null }; } return { done: false, value: result.value }; }, }; }, }; return mockReadable; }, }; const reader = completionStream.toReadableStream().getReader(); let receivedContents = []; while (true) { const { done, value } = await reader.read(); if (done) break; // 处理结束状态,退出循环 // 解码数据并断言内容 const chunk = new util.TextDecoder("utf-8").decode(value); const data = JSON.parse(chunk); receivedContents.push(data.choices[0].delta.content); expect(done).toBe(false); } // 最终断言完整内容 expect(receivedContents.join("")).toBe("Hellothere"); }); });
关键优化点
- 预先创建异步迭代器实例,让所有
read调用复用同一个迭代器,确保能正常推进。 - 移除多余的
yield null,依赖迭代器自然结束的状态。 - 在循环中检测
done状态并退出,避免无限循环。 - 添加了解码和最终内容的断言,让测试逻辑更完整。
内容的提问来源于stack exchange,提问作者user686483
相关产品推荐
相关产品推荐

