admin管理员组文章数量:1287986
I have a Web Serial port that I want to read some data from. I'd like to use TransformStream
s to do some processing (e.g. decode bytes to strings, separate out logical messages, etc) by using pipeThrough
to transform the data. However, once I do this, I can no longer release the lock on the port by calling reader.releaseLock()
. What am I doing wrong here?
This code works how I expect (running without dependencies in the browser in a secure context):
async serialTestWorking() {
const port = await navigator.serial.requestPort();
await port.open({baudRate: 9600});
console.log("Is locked after open?", port.readable.locked);
// Prints "Is locked after open? false"
let reader = port.readable.getReader();
console.log("Is locked after getReader?", port.readable.locked);
// Prints "Is locked after getReader? true"
reader.releaseLock();
console.log("Is locked after releaseLock?", port.readable.locked);
// Prints "Is locked after releaseLock? false"
await port.close();
console.log("Port closed");
// Prints "Port closed"
}
However, if I use pipeThrough
to send the output through a do-nothing TransformStream
, it all falls apart. The lock isn't released at releaseLock
and the final close
fails to work.
async serialTestBroken() {
const port = await navigator.serial.requestPort();
await port.open({baudRate: 9600});
console.log("Is locked after open?", port.readable.locked);
// Prints "Is locked after open? false"
let reader = port.readable.pipeThrough(new TransformStream()).getReader();
console.log("Is locked after getReader?", port.readable.locked);
// Prints "Is locked after getReader? true"
reader.releaseLock();
console.log("Is locked after releaseLock?", port.readable.locked);
// Prints "Is locked after releaseLock? true"
await port.close();
console.log("Port closed");
// Doesn't make it to the log line
//throws "TypeError: Failed to execute 'close' on 'SerialPort': Cannot cancel a locked stream"
}
What am I doing wrong here? Does releasing the lock on the TransformStream
really not propagate upstream? Do I have to keep track of an instance of every transformer in my pipeline so I can be sure to unlock them all?
The streams spec says that piping locks the readable and writable streams for the duration of the pipe operation.
Piping locks the readable and writable streams, preventing them from being manipulated for the duration of the pipe operation. This allows the implementation to perform important optimizations, such as directly shuttling data from the underlying source to the underlying sink while bypassing many of the intermediate queues.
Is there some other way I have to indicate that the "piping operation" is pleted?
I have a Web Serial port that I want to read some data from. I'd like to use TransformStream
s to do some processing (e.g. decode bytes to strings, separate out logical messages, etc) by using pipeThrough
to transform the data. However, once I do this, I can no longer release the lock on the port by calling reader.releaseLock()
. What am I doing wrong here?
This code works how I expect (running without dependencies in the browser in a secure context):
async serialTestWorking() {
const port = await navigator.serial.requestPort();
await port.open({baudRate: 9600});
console.log("Is locked after open?", port.readable.locked);
// Prints "Is locked after open? false"
let reader = port.readable.getReader();
console.log("Is locked after getReader?", port.readable.locked);
// Prints "Is locked after getReader? true"
reader.releaseLock();
console.log("Is locked after releaseLock?", port.readable.locked);
// Prints "Is locked after releaseLock? false"
await port.close();
console.log("Port closed");
// Prints "Port closed"
}
However, if I use pipeThrough
to send the output through a do-nothing TransformStream
, it all falls apart. The lock isn't released at releaseLock
and the final close
fails to work.
async serialTestBroken() {
const port = await navigator.serial.requestPort();
await port.open({baudRate: 9600});
console.log("Is locked after open?", port.readable.locked);
// Prints "Is locked after open? false"
let reader = port.readable.pipeThrough(new TransformStream()).getReader();
console.log("Is locked after getReader?", port.readable.locked);
// Prints "Is locked after getReader? true"
reader.releaseLock();
console.log("Is locked after releaseLock?", port.readable.locked);
// Prints "Is locked after releaseLock? true"
await port.close();
console.log("Port closed");
// Doesn't make it to the log line
//throws "TypeError: Failed to execute 'close' on 'SerialPort': Cannot cancel a locked stream"
}
What am I doing wrong here? Does releasing the lock on the TransformStream
really not propagate upstream? Do I have to keep track of an instance of every transformer in my pipeline so I can be sure to unlock them all?
The streams spec says that piping locks the readable and writable streams for the duration of the pipe operation.
Piping locks the readable and writable streams, preventing them from being manipulated for the duration of the pipe operation. This allows the implementation to perform important optimizations, such as directly shuttling data from the underlying source to the underlying sink while bypassing many of the intermediate queues.
Is there some other way I have to indicate that the "piping operation" is pleted?
Share Improve this question edited Feb 25, 2022 at 7:37 ddulaney asked Feb 25, 2022 at 7:28 ddulaneyddulaney 1,1019 silver badges25 bronze badges 3- 2 Does web.dev/serial/#transforming-streams help? – François Beaufort Commented Feb 25, 2022 at 8:18
- It helps a ton. Thanks! – ddulaney Commented Feb 26, 2022 at 2:18
- await reader.cancel(); await port.close(); – Hoxz Commented Dec 25, 2024 at 18:39
2 Answers
Reset to default 9As explained in https://web.dev/serial/#close-port, port.close()
closes the serial port if its readable
and writable
members are unlocked, meaning releaseLock()
has been called for their respective reader and writer.
Closing a serial port is a bit more plicated when using transform streams though. Call reader.cancel()
, then call writer.close()
and port.close()
. This propagates errors through the transform streams to the underlying serial port. Because error propagation doesn't happen immediately, you need to use the readableStreamClosed
and writableStreamClosed
promises created earlier to detect when port.readable
and port.writable
have been unlocked. Cancelling the reader causes the stream to be aborted; this is why you must catch and ignore the resulting error.
const textDecoder = new TextDecoderStream();
const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
const reader = textDecoder.readable.getReader();
// Listen to data ing from the serial device.
while (true) {
const { value, done } = await reader.read();
if (done) {
reader.releaseLock();
break;
}
// value is a string.
console.log(value);
}
// Later...
const textEncoder = new TextEncoderStream();
const writer = textEncoder.writable.getWriter();
const writableStreamClosed = textEncoder.readable.pipeTo(port.writable);
reader.cancel();
await readableStreamClosed.catch(() => { /* Ignore the error */ });
writer.close();
await writableStreamClosed;
await port.close();
To whom it might concern, I've also found this solution which worked until the point it didn't anymore. The await thing.catch(Object)
as no-op was spot-on (and awkward at the same time) but in general, after 1 hour messing around I've ust came to the conclusion that the easiest way to close a port and free everything is by passing an AbortController.signal.
The pipeTo indeed accepts a second options
parameter which in turns accepts a signal
property so that this is what I ended up doing and finally freed properly and closed the port:
const writable = new WritableStream({ ... });
// pass { signal: aborter.signal } or just aborter
const aborter = new AbortController;
const readerClosed = port.readable.pipeTo(writable, aborter);
// later on ...
aborter.abort(); // that's it ...
// the rest of the dance
writer.close();
await writerClosed;
await readerClosed.catch(Object);
await port.close();
I hope this hint will help those that went as nuts as I did this morning.
本文标签: javascriptHow can I close a Web Serial port that I39ve piped through a TransformStreamStack Overflow
版权声明:本文标题:javascript - How can I close a Web Serial port that I've piped through a TransformStream? - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741334795a2372979.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论