当 Reader“挂起”时如何中断它(Reader.read() 需要超时)

Jin*_*ska 4 javascript google-chrome serial-port webapi

这个问题与使用 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
}
Run Code Online (Sandbox Code Playgroud)

serialPort是返回的对象navigator.serial.requestPort()

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

PS在真实的app中serialPort是一个全局变量,但这并不重要,不是吗?

T.J*_*der 6

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

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

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

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

Promise.race观察承诺的竞赛,根据它看到的您提供的数组中的承诺的第一个解决方案来确定其承诺。因此,如果read的 Promise 在超时 Promise 拒绝之前得到履行(或拒绝),read则 的结算决定了 Promise 的结算race。否则,race承诺将根据超时承诺的解决(在本例中为拒绝)进行结算。