1- import { batch } from "../batch" ;
1+ import { batch , type BatchTask , tasks , toTask } from "../batch" ;
22import { type AddEventListener , event , send , size } from "../event" ;
33import { writable } from "../readable" ;
44import { type Disposer , type OwnedWritable , type Readable } from "../typings" ;
@@ -24,65 +24,53 @@ export class OwnedReactiveMap<K, V> extends Map<K, V> {
2424 * @returns A disposer function to unsubscribe from the event.
2525 */
2626 public onChanged ( fn : ( changed : ReactiveMapChanged < K , V > ) => void ) : Disposer {
27- if ( ! this . _onChanged_ ) {
28- this . _onChanged_ = event ( { delete : new Set ( ) , upsert : new Map ( ) } ) ;
29- const handler = ( ) => {
30- if ( this . _onChanged_ && size ( this . _onChanged_ ) ) {
31- const { data_ } = this . _onChanged_ ;
32- if ( data_ . upsert . size > 0 || data_ . delete . size > 0 ) {
33- const changedData = {
34- upsert : [ ...data_ . upsert ] ,
35- delete : [ ...data_ . delete ] ,
36- } ;
37- data_ . upsert . clear ( ) ;
38- data_ . delete . clear ( ) ;
39- send ( this . _onChanged_ , changedData ) ;
40- }
41- } else {
42- this . _onChanged_ = undefined ;
43- this . $ . unsubscribe ( handler ) ;
27+ return ( this . _onChanged_ ??= toTask ( event ( { delete : new Set ( ) , upsert : new Map ( ) } ) , ( ) => {
28+ if ( this . _onChanged_ && size ( this . _onChanged_ ) ) {
29+ const { data_ } = this . _onChanged_ ;
30+ if ( data_ . upsert . size > 0 || data_ . delete . size > 0 ) {
31+ const changedData = {
32+ upsert : [ ...data_ . upsert ] ,
33+ delete : [ ...data_ . delete ] ,
34+ } ;
35+ data_ . upsert . clear ( ) ;
36+ data_ . delete . clear ( ) ;
37+ send ( this . _onChanged_ , changedData ) ;
4438 }
45- } ;
46- this . $ . onReaction_ ( handler ) ;
47- }
48- return this . _onChanged_ ( fn ) ;
39+ } else {
40+ this . _onChanged_ = undefined ;
41+ }
42+ } ) ) ( fn ) ;
4943 }
5044
5145 /**
52- * Subscribe to value removal events.
53- * This is useful for further processing values that are removed from the map.
46+ * Subscribe to events when a value is needed to be disposed.
5447 *
55- * A value is considered removed when:
48+ * A value is considered for disposal when:
5649 * - it is deleted from the map.
5750 * - it is replaced by another value (the old value is removed).
5851 * - it is cleared from the map.
52+ * - the map is disposed.
5953 *
6054 * Note that for performance reasons, it does not handle the case where multiple keys map to the same value.
6155 *
62- * @param fn - The function to call when a value is removed .
56+ * @param fn - The function to call when a value is needed to be disposed .
6357 * @returns A disposer function to unsubscribe from the event.
6458 */
65- public onValueRemoved ( fn : ( value : V ) => void ) : Disposer {
66- if ( ! this . _onValueRemoved_ ) {
67- this . _onValueRemoved_ = event ( new Set < V > ( ) ) ;
68- const handler = ( ) => {
69- if ( this . _onValueRemoved_ && size ( this . _onValueRemoved_ ) ) {
70- const { data_ } = this . _onValueRemoved_ ;
71- if ( data_ . size ) {
72- const removedValues = [ ...data_ ] ;
73- data_ . clear ( ) ;
74- for ( const value of removedValues ) {
75- send ( this . _onValueRemoved_ , value ) ;
76- }
59+ public onDisposeValue ( fn : ( value : V ) => void ) : Disposer {
60+ return ( this . _onDisposeValue_ ??= toTask ( event ( new Set < V > ( ) ) , ( ) => {
61+ if ( this . _onDisposeValue_ && size ( this . _onDisposeValue_ ) ) {
62+ const { data_ } = this . _onDisposeValue_ ;
63+ if ( data_ . size ) {
64+ const removedValues = [ ...data_ ] ;
65+ data_ . clear ( ) ;
66+ for ( const value of removedValues ) {
67+ send ( this . _onDisposeValue_ , value ) ;
7768 }
78- } else {
79- this . _onValueRemoved_ = undefined ;
80- this . $ . unsubscribe ( handler ) ;
8169 }
82- } ;
83- this . $ . onReaction_ ( handler ) ;
84- }
85- return this . _onValueRemoved_ ( fn ) ;
70+ } else {
71+ this . _onDisposeValue_ = undefined ;
72+ }
73+ } ) ) ( fn ) ;
8674 }
8775
8876 public constructor ( entries ?: readonly ( readonly [ K , V ] ) [ ] | null ) {
@@ -98,16 +86,32 @@ export class OwnedReactiveMap<K, V> extends Map<K, V> {
9886 }
9987
10088 public dispose ( ) : void {
101- this . _$ ?. dispose ( ) ;
102- this . _onChanged_ = this . _onValueRemoved_ = undefined ;
103- super . clear ( ) ;
89+ if ( this . _disposed_ ) return ;
90+ if ( process . env . NODE_ENV !== "production" ) {
91+ this . _disposed_ = new Error ( "[embra] ReactiveMap disposed at:" ) ;
92+ } else {
93+ this . _disposed_ = true ;
94+ }
95+ if ( this . _onDisposeValue_ ) {
96+ const { data_ } = this . _onDisposeValue_ ;
97+ for ( const value of this . values ( ) ) {
98+ data_ . add ( value ) ;
99+ }
100+ if ( data_ . size ) {
101+ batch ( ( ) => {
102+ tasks . add ( this . _onDisposeValue_ ! ) ;
103+ } ) ;
104+ }
105+ }
106+ this . _$ = this . _onChanged_ = this . _onDisposeValue_ = undefined ;
104107 }
105108
106109 public override set ( key : K , value : V ) : this {
107110 if ( this . has ( key ) ) {
108111 const oldValue = this . get ( key ) ! ;
109112 if ( ! strictEqual ( oldValue , value ) ) {
110- this . _onValueRemoved_ ?. data_ . add ( oldValue ) ;
113+ // task added in this._upsert_
114+ this . _onDisposeValue_ ?. data_ . add ( oldValue ) ;
111115 this . _upsert_ ( key , value ) ;
112116 }
113117 } else {
@@ -118,25 +122,37 @@ export class OwnedReactiveMap<K, V> extends Map<K, V> {
118122
119123 public override delete ( key : K ) : boolean {
120124 if ( this . has ( key ) ) {
121- this . _onValueRemoved_ ?. data_ . add ( this . get ( key ) ! ) ;
122- this . _onChanged_ ?. data_ . delete . add ( key ) ;
123- this . _onChanged_ ?. data_ . upsert . delete ( key ) ;
124- this . _$ ?. set ( this ) ;
125+ if ( this . _onDisposeValue_ ) {
126+ this . _onDisposeValue_ . data_ . add ( this . get ( key ) ! ) ;
127+ tasks . add ( this . _onDisposeValue_ ) ;
128+ }
129+ if ( this . _onChanged_ ) {
130+ this . _onChanged_ . data_ . delete . add ( key ) ;
131+ this . _onChanged_ . data_ . upsert . delete ( key ) ;
132+ tasks . add ( this . _onChanged_ ) ;
133+ }
134+ this . _notify_ ( ) ;
125135 }
126136 return super . delete ( key ) ;
127137 }
128138
129139 public override clear ( ) : void {
130140 if ( this . size ) {
131- if ( this . _onValueRemoved_ || this . _onChanged_ ) {
141+ if ( this . _onDisposeValue_ || this . _onChanged_ ) {
132142 for ( const [ key , value ] of this ) {
133- this . _onValueRemoved_ ?. data_ . add ( value ) ;
134- this . _onChanged_ ?. data_ . delete . add ( key ) ;
135- this . _onChanged_ ?. data_ . upsert . delete ( key ) ;
143+ if ( this . _onDisposeValue_ ) {
144+ this . _onDisposeValue_ . data_ . add ( value ) ;
145+ tasks . add ( this . _onDisposeValue_ ) ;
146+ }
147+ if ( this . _onChanged_ ) {
148+ this . _onChanged_ . data_ . delete . add ( key ) ;
149+ this . _onChanged_ . data_ . upsert . delete ( key ) ;
150+ tasks . add ( this . _onChanged_ ) ;
151+ }
136152 }
137153 }
138154 super . clear ( ) ;
139- this . _$ ?. set ( this ) ;
155+ this . _notify_ ( ) ;
140156 }
141157 }
142158
@@ -150,27 +166,51 @@ export class OwnedReactiveMap<K, V> extends Map<K, V> {
150166 } ) ;
151167 }
152168
169+ /**
170+ * @internal
171+ */
172+ private _disposed_ ?: Error | true ;
173+
153174 /** @internal */
154175 private _$ ?: OwnedWritable < this> ;
155176
156177 /** @internal */
157- private _onChanged_ ?: AddEventListener <
158- ReactiveMapChanged < K , V > ,
159- {
160- readonly upsert : Map < K , V > ;
161- readonly delete : Set < K > ;
162- }
178+ private _onChanged_ ?: BatchTask <
179+ AddEventListener <
180+ ReactiveMapChanged < K , V > ,
181+ {
182+ readonly upsert : Map < K , V > ;
183+ readonly delete : Set < K > ;
184+ }
185+ >
163186 > ;
164187
165188 /** @internal */
166- private _onValueRemoved_ ?: AddEventListener < V , Set < V > > ;
189+ private _onDisposeValue_ ?: BatchTask < AddEventListener < V , Set < V > > > ;
167190
168191 /** @internal */
169192 private _upsert_ ( key : K , value : V ) : void {
170- this . _onValueRemoved_ ?. data_ . delete ( value ) ;
171- this . _onChanged_ ?. data_ . upsert . set ( key , value ) ;
172- this . _onChanged_ ?. data_ . delete . delete ( key ) ;
193+ if ( this . _onDisposeValue_ ) {
194+ this . _onDisposeValue_ . data_ . delete ( value ) ;
195+ tasks . add ( this . _onDisposeValue_ ) ;
196+ }
197+ if ( this . _onChanged_ ) {
198+ this . _onChanged_ . data_ . upsert . set ( key , value ) ;
199+ this . _onChanged_ . data_ . delete . delete ( key ) ;
200+ tasks . add ( this . _onChanged_ ) ;
201+ }
173202 super . set ( key , value ) ;
203+ this . _notify_ ( ) ;
204+ }
205+
206+ /** @internal */
207+ private _notify_ ( ) {
208+ if ( this . _disposed_ ) {
209+ console . error ( new Error ( "disposed" ) ) ;
210+ if ( process . env . NODE_ENV !== "production" ) {
211+ console . error ( this . _disposed_ ) ;
212+ }
213+ }
174214 this . _$ ?. set ( this ) ;
175215 }
176216}
0 commit comments