这个问题与使用 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
是一个全局变量,但这并不重要,不是吗?
我认为
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
承诺将根据超时承诺的解决(在本例中为拒绝)进行解决。
可以“破坏”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);
根据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())