如何在“挂起”时中断阅读器需要在 Reader.read() 上超时

问题描述

这个问题与使用 Chrome Serial API 时发生的情况有关,但可能与任何 ReadableStream 相关。我研究了文档,可能遗漏了一些功能或模式。

Chrome 浏览器中正在运行一个简单的程序,访问 CW 键控器(基于 Arduino,但这并不重要)。

应用程序向键控器发送命令,并需要两个二进制字节或字符串作为响应(具体格式取决于发送的命令,并不重要)。

如果串行设备(不是 USB/串行适配器,而是 Arduino)因任何原因错过命令,则永远不会发送响应,并且下面的函数 expectResponse() 永远不会返回任何数据,也不会抛出任何异常。结果,Reader 保持锁定状态,因此无法关闭 ReadableStream,因此也无法关闭串行端口。

此外,根据应用程序结构,如果其他命令成功发送到键控器,可能无法读取第二个响应,因为第一个读取器阻塞了流,直到它被释放,才能创建新的读取器.


async function expectResponse( serialPort ) {
   const reader = serialPort.readable.getReader() ;
   let { value,done } = await reader.read() ; // this never returns because no data arrive,not possible to "break"
}

async function disconnect( serialPort ) {
   // ... some cleanup ...
   // naive attempt to unlock ReadableStream before closing 
   await serialPort.readable.getReader().releaseLock() // this will throw exception - cannot create  new reader while another one is still active and locks the stream
   // ...
   await serialPort.close(); // this will throw exception - cannot close port because readable stream is locked
}

serialPortnavigator.serial.requestPort()

返回的对象

我确信我一定遗漏了 API 文档(ReadableStreamReader API,而不是 Serial API)中的一些重要内容,但我没有找到解决方案。

附言在实际应用中,serialPort一个全局变量,但没关系,是吗?

解决方法

我认为 ReadableStream 没有内置超时功能。

我会使用 Promise.race,另一个承诺是您的超时:

let { value,done } = await Promise.race([
    reader.read(),new Promise((_,reject) => setTimeout(reject,TIMEOUT,new Error("timeout")))
]);

(您可能会将 new Promise 代码放入实用程序函数中。)

Promise.race 观察 promises 的竞争,根据它在你给它的数组中看到的 promises 的第一个结算来解决它的 promises。因此,如果 read 的承诺在超时承诺拒绝之前得到履行(或拒绝),则 read 的结算决定了 race 承诺的结算。否则,race 承诺基于超时承诺的解决(在本例中为拒绝)。