Streams API: possible to undo .pipeTo()?

1k views Asked by At

The Streams API provides a neat way of piping a ReadableStream to a WritableStream using readableStream.pipeTo(writableStream). This would appear to be more convenient than obtaining readableStream.getReader() and manually gluing it to writableStream.getWriter() or directly to underlying functionality.

I have a use case where I am provided a ReadableStream object which may eventually need to "go away" and be replaced by a new ReadableStream object. It would be nice to use a single custom WritableStream to wrap the functionality which these ReadableStream objects interface with and pipe them together for as long as needed, but have not found how to "undo" the piping process.

Q: Is it possible to "undo"/"break"/"teardown" a pipe created with .pipeTo(), or are pipes "permanent" and not appropriate for this use case?

Example using Serial API, where I would like to be able to call doConnectPort() and then doDisconnectPort() repeatedly:

var customWritable = new WritableStream(…);
var port;

async function doConnectPort() {
  port = await navigator.serial.requestPort();
  await port.open({ baudrate: …});
  await port.readable.pipeTo(customWritable);
}

async function doDisconnectPort() {
  // What to do here? Can't close the port yet—port.readable is still locked!
  await port.close();
}

Currently, I manually glue things together and clean up when it's time to disconnect the port:

var port;
var portReader;

async function doConnectPort() {
  port = await navigator.serial.requestPort();
  await port.open({ baudrate: …});
  portReader = port.readable.getReader();
  while (true) {
    const { value, done } = await portReader.read();
    if (done) {
      break;
    }
    // do something with value
  }
}

async function doDisconnectPort() {
  await portReader.cancel();
  await portReader.releaseLock();
  await port.close();
}
1

There are 1 answers

1
Kaiido On BEST ANSWER

You can pass an AbortSignal in the pipeTo's option object's signal.

const btn = document.querySelector( "button" );
const checkbox = document.querySelector( "input" );

if( !ReadableStream.prototype.pipeTo ) {
  console.warn( "Your browser doesn't support pipeTo" );
}
else {

btn.onclick = (evt) => {

  console.clear( "" );
  const blob = new Blob( [ "hello" ] );
  const readable = blob.stream();
  const target = new TransformStream();
  const abort_controller = new AbortController();

  target.readable.getReader().read().then( console.log );

  readable.pipeTo( target.writable, { signal: abort_controller.signal } )
    .catch( console.error )

  if( checkbox.checked ) {
    abort_controller.abort();
  }
  
};

}
<label>Abort pipe<input type="checkbox" checked></label><br>
<button id="btn">new test</button>