You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 06:16:08