web-streams-extensions
Version:
A comprehensive collection of helper methods for WebStreams with built-in backpressure support, inspired by ReactiveExtensions
109 lines (108 loc) • 3.38 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Subscribable = void 0;
/**
* Internal class that manages multiple subscribers and handles value distribution.
* Supports backpressure by returning the minimum desired size from all subscribers.
*
* @template T The type of values handled by this subscribable
*/
class Subscribable {
closed = false;
subscribers = [];
/**
* Subscribe to this subscribable with the provided subscriber callbacks.
*
* @param cb - Subscriber with next, complete, and error callbacks
* @returns A subscription that can be used to unsubscribe
*/
subscribe(cb) {
let self = this;
if (self.closed) {
// If already closed, immediately call complete
cb.complete();
return {
get closed() { return true; },
unsubscribe() { }
};
}
self.subscribers.push(cb);
let _closed = false;
return {
get closed() { return _closed || self.closed; },
unsubscribe() {
let index = self.subscribers.findIndex(x => x === cb);
if (index >= 0) {
self.subscribers.splice(index, 1);
}
_closed = true;
}
};
}
/**
* Send a value to all subscribers and return the minimum desired size.
* This enables backpressure handling by allowing the source to know when to slow down.
*
* @param value - The value to send to all subscribers
* @returns The minimum desired size from all subscribers (0 or higher means continue, negative means pause)
*/
next(value) {
if (this.closed || this.subscribers.length === 0) {
return 0;
}
const desiredSizes = this.subscribers.map(subscriber => {
try {
return subscriber.next(value);
}
catch (err) {
// If a subscriber throws, handle the error gracefully
try {
subscriber.error(err);
}
catch (e) {
// If error handling also fails, remove the subscriber
}
return 0;
}
});
// Return the minimum desired size to implement proper backpressure
return Math.min(...desiredSizes);
}
/**
* Complete all subscribers and close this subscribable.
*/
complete() {
if (this.closed)
return;
for (let sub of this.subscribers) {
try {
sub.complete();
}
catch (err) {
// Ignore errors during completion
}
}
this.subscribers = [];
this.closed = true;
}
/**
* Send an error to all subscribers and close this subscribable.
*
* @param err - The error to send to all subscribers
*/
error(err) {
if (this.closed)
return;
for (let sub of this.subscribers) {
try {
sub.error(err);
}
catch (e) {
// Ignore errors during error handling
}
}
this.subscribers = [];
this.closed = true;
}
}
exports.Subscribable = Subscribable;