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

问题描述 投票:0回答:3

这个问题与使用 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
}

serialPort
navigator.serial.requestPort()

返回的对象

我确信我一定错过了 API 文档中的一些重要内容(

ReadableStream
Reader
API,而不是
Serial
API),但我没有找到解决方案。

附注在真实的应用程序中

serialPort
是一个全局变量,但这并不重要,不是吗?

javascript google-chrome serial-port webapi
3个回答
6
投票

我认为

ReadableStream
没有内置超时功能。

我会使用

Promise.race
另一个承诺是你的超时:

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

(您可能会将

new Promise
代码放入实用程序函数中。)

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


1
投票

可以“破坏”await 操作:

根据文档,如果您调用

await reader.read()
TypeError
将抛出
reader.releaseLock()
。 (例如使用
setTimeout

文档: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader/read

try {
    await reader.read();
} catch(e) {
    console.log("you got out from await");
}

setTimeout(function() {
    reader.releaseLock();
}, TIMEOUT);

0
投票

根据TJ的回答,我想出了这个:

function promiseTimeout<T>(promise: Promise<T>, ms = 500): Promise<T> {
    let timer: any
    const timeoutPromise = new Promise<T>((_, reject) => {
        timer = setTimeout(() => reject(new Error(`Promise timed out after ${ms}ms`)), ms)
    })
    promise.finally(() => {
        clearTimeout(timer)
    })
    return Promise.race([promise, timeoutPromise])
}

你可以像这样使用:

let {value: chunk, done: readerDone} = await promiseTimeout(reader.read())
© www.soinside.com 2019 - 2024. All rights reserved.