Deno读取COM端口无数据时阻塞,如何判断端口无数据?
在Deno中读取COM端口时避免阻塞并判断无数据
核心问题说明
你当前的代码没捕获file.read()的返回值,而且COM端口属于非常规文件:只要端口没关闭,它不会主动触发EOF(也就是read()不会返回null),没数据时read()会一直阻塞。另外你的路径写法有问题,Windows下COM端口路径需要转义为\\\\.\\COMx,不然会打开失败。
解决方案
方案1:添加超时机制(推荐)
用Promise.race()把读取操作和超时Promise绑定,超时后直接判定为无数据,同时捕获read()返回的字节数,准确获取实际读取的内容:
修改后的COM类代码:
export class COM { #file?: Deno.File; COMPort: number; constructor(COMPort: number) { this.COMPort = COMPort; } async open(): Promise<void> { // 修正Windows下COM端口路径 this.#file = await Deno.open(`\\\\.\\COM${this.COMPort}`, { read: true, write: true }); } close(): void { this.#file?.close(); } // 封装带超时的读取逻辑 async #readWithTimeout(buffer: Uint8Array, timeoutMs: number): Promise<number | null> { if (!this.#file) throw new Error("COM端口未打开"); const readPromise = this.#file.read(buffer); const timeoutPromise = new Promise<null>((resolve) => setTimeout(() => resolve(null), timeoutMs) ); return Promise.race([readPromise, timeoutPromise]); } async readChar(chars: number, timeoutMs = 1000): Promise<Uint8Array | null> { if (!this.#file) throw new Error("COM端口未打开"); const buffer = new Uint8Array(chars); const bytesRead = await this.#readWithTimeout(buffer, timeoutMs); // 超时或未读到数据时返回null if (bytesRead === null || bytesRead === 0) { return null; } // 返回实际读取的有效字节(去掉未填充的空字节) return buffer.slice(0, bytesRead); } }
主文件代码修改:
const decoder = new TextDecoder(); const com = new COM(4); await com.open(); while (true) { const result = await com.readChar(1); if (result === null) { console.log("无更多数据,退出循环"); break; } console.log(result, decoder.decode(result)); } com.close();
方案2:非阻塞模式读取
给Deno.open添加非阻塞标志,让read()无数据时立即返回0,以此判断无数据:
修改COM类的open和读取方法:
async open(): Promise<void> { const O_NONBLOCK = 0x4000; // Windows平台的非阻塞标志 this.#file = await Deno.open(`\\\\.\\COM${this.COMPort}`, { read: true, write: true, flags: Deno.O_RDWR | O_NONBLOCK }); } async readChar(chars: number): Promise<Uint8Array | null> { if (!this.#file) throw new Error("COM端口未打开"); const buffer = new Uint8Array(chars); const bytesRead = await this.#file.read(buffer); if (bytesRead === 0) { return null; } return buffer.slice(0, bytesRead); }
主文件循环里要加个小延迟,避免CPU占用过高:
while (true) { const result = await com.readChar(1); if (result === null) { console.log("无更多数据,退出循环"); break; } console.log(result, decoder.decode(result)); await new Promise(resolve => setTimeout(resolve, 50)); // 降低循环频率 }
关键提醒
- 串口的EOF只有在端口关闭时才会触发,别指望用
read()返回null判断无数据,必须靠超时或非阻塞模式。 - Windows下COM端口路径必须转义成
\\\\.\\COMx,原代码的\\.\\COMx会被解析成错误路径,导致打开失败。
内容的提问来源于stack exchange,提问作者Mqx
相关产品推荐
相关产品推荐

