Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 593x 593x 145x 145x 75x 75x 145x 145x 99x 99x 145x 145x 188x 188x 145x 145x 145x 233x 233x 145x 145x 220x 220x 145x 145x 145x 191x 191x 34x 34x 157x 157x 191x 145x 145x 41x 41x 145x 145x 145x 273x 273x 273x 3x 3x 3x 145x 145x 145x 145x 231x 231x 231x 231x 230x 178x 178x 178x 145x 145x 145x 145x 145x 145x 145x 145x 145x 145x 148x 148x 145x 145x | /**
* A list of subscribers with safe fan-out.
*
* Every registry we keep - contact watchers, the store's asset watchers,
* socket connection listeners - hands events to page components we don't
* control, so they all need the same three things: one subscriber throwing
* can't cost the others their delivery, a delivery iterates a snapshot so
* unsubscribing mid-fan-out doesn't shift the list underneath it, and
* unsubscribing twice does nothing the second time.
*
* The subscriber record and how an event reaches it belong to the caller -
* interests, payloads and arities differ - so this owns only the list and the
* delivery discipline.
*/
export class Watchers<W> {
private watchers: W[] = [];
// names the subscriber when reporting one that threw
private label: string;
constructor(label: string) {
this.label = label;
}
public get size(): number {
return this.watchers.length;
}
public has(watcher: W): boolean {
return this.watchers.includes(watcher);
}
public some(predicate: (watcher: W) => boolean): boolean {
return this.watchers.some(predicate);
}
/** A snapshot, for callers that need to read across everyone registered. */
public all(): W[] {
return [...this.watchers];
}
public add(watcher: W): void {
this.watchers.push(watcher);
}
/** Removes it, reporting whether it was still registered. */
public remove(watcher: W): boolean {
const index = this.watchers.indexOf(watcher);
if (index < 0) {
return false;
}
this.watchers.splice(index, 1);
return true;
}
public clear(): void {
this.watchers.length = 0;
}
/** One delivery, with a subscriber's failure kept to itself. */
public deliver(watcher: W, deliver: (watcher: W) => void): void {
try {
deliver(watcher);
} catch (error) {
console.error(`${this.label} failed`, error);
}
}
/** Delivers to everyone registered now, or just those matching. */
public each(
deliver: (watcher: W) => void,
matches?: (watcher: W) => boolean
): void {
for (const watcher of this.all()) {
if (!matches || matches(watcher)) {
this.deliver(watcher, deliver);
}
}
}
/**
* An initial delivery to one subscriber, off the current task so it lands
* the way a live one would rather than before they can use the handle they
* are being given. Skipped if they leave before it lands.
*/
public prime(watcher: W, deliver: (watcher: W) => void): void {
Promise.resolve().then(() => {
if (this.has(watcher)) {
this.deliver(watcher, deliver);
}
});
}
}
|