web-streams-extensions
Version:
A comprehensive collection of helper methods for WebStreams with built-in backpressure support, inspired by ReactiveExtensions
110 lines (109 loc) • 3.71 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.subscribe = subscribe;
const _readable_like_js_1 = require("./_readable-like.cjs");
/**
* Subscribe to a ReadableStream with callbacks for next, complete, and error events.
* Immediately begins reading from the source stream and calls the appropriate callbacks.
*
* @template T The type of values in the stream
* @param src The ReadableStream or ReadableLike to subscribe to
* @param next Callback called for each value in the stream
* @param complete Optional callback called when the stream completes
* @param error Optional callback called when the stream encounters an error
* @returns A subscription that can be used to unsubscribe and stop reading
*
* @example
* ```typescript
* let src = from(function*() { yield 1; yield 2; yield 3; });
*
* subscribe(src,
* (next) => { console.log("Next:", next); },
* () => { console.log("Complete"); },
* (err) => { console.log("Error:", err); }
* );
* ```
*/
function subscribe(src, next, complete, error) {
if ((0, _readable_like_js_1.isReadableLike)(src)) {
src = src.readable;
}
let reader = src.getReader();
let isClosed = false;
let isErroring = false; // Track if we're in an error state
let sub = {
get closed() { return isClosed; },
unsubscribe() {
if (!isClosed && reader) {
try {
reader.cancel().catch(() => { }); // Handle async cancellation properly
reader.releaseLock();
// Only call complete if this is a normal unsubscribe, not due to error
if (complete && !isErroring)
complete();
}
catch (err) {
console.debug("cleanup error", err);
// Ignore cleanup errors
}
finally {
reader = null;
}
}
isClosed = true;
}
};
async function start() {
try {
while (reader && !isClosed) {
let chunk = await reader.read();
if (chunk.done) {
reader.releaseLock();
reader = null;
if (complete && !isClosed) {
try {
complete();
}
catch (err) {
// Ignore errors in complete callback
}
}
break;
}
else {
try {
await next(chunk.value);
}
catch (err) {
// If next callback throws, treat it as an error
isErroring = true;
if (error && !isClosed) {
try {
error(err);
}
catch (e) {
// Ignore errors in error callback
}
}
sub.unsubscribe();
break;
}
}
}
}
catch (err) {
if (error && !isClosed) {
try {
error(err);
}
catch (e) {
// Ignore errors in error callback
}
}
isErroring = true;
sub.unsubscribe();
}
}
start();
return sub;
}