fast reactive signals jsr.io/@mary/signals
typescript jsr
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: clean up more things

Mary fe253a2c b3fff573

+57 -24
+42 -24
mod.ts
··· 58 58 */ 59 59 let read_clock = 0; 60 60 61 + /** 62 + * increments the batch depth. 63 + * when the depth is greater than 0, effects are not run immediately. 64 + */ 61 65 function start_batch(): void { 62 66 batch_depth++; 63 67 } 64 68 69 + /** 70 + * decrements the batch depth. 71 + * when the depth reaches 0, it runs all batched effects. 72 + */ 65 73 function end_batch(): void { 66 74 if (batch_depth > 1) { 67 75 batch_depth--; ··· 105 113 } 106 114 } 107 115 116 + /** 117 + * checks if a computation is stale. 118 + * @param target the computation to check 119 + * @param flags the flags of the computation 120 + * @returns `true` if the computation is stale, `false` otherwise 121 + */ 108 122 function is_stale(target: Computation, flags: number): boolean { 109 123 const dependencies = target._dependencies; 110 124 ··· 125 139 return false; 126 140 } 127 141 142 + /** 143 + * finalizes the dependencies of the currently evaluating computation. 144 + * this is where we subscribe to new dependencies and unsubscribe from old ones. 145 + */ 128 146 function finalize_dependencies(): void { 129 147 let dependencies = eval_listener!._dependencies; 130 148 131 149 if (eval_untracked_sources) { 132 - // we have new dependencies, so let's unsubscribe from stale dependencies. 150 + // new dependencies were added, so we need to unsubscribe from stale ones. 133 151 prune_old_dependencies(); 134 152 135 153 if (eval_sources_index > 0) { 136 - // we have existing dependencies still depended on, so let's expand the 137 - // existing array to make room for our new dependencies. 154 + // some of the old dependencies are still active. 155 + // so we need to combine the old and new dependencies. 138 156 const ilen = eval_untracked_sources.length; 139 157 140 158 dependencies.length = eval_sources_index + ilen; 141 159 142 - // override anything after the pointer with our new dependencies, this is 143 - // fine since we're no longer subscribed to them. 160 + // override anything after the pointer with our new dependencies. 161 + // this is fine since we're no longer subscribed to them. 144 162 for (let i = 0; i < ilen; i++) { 145 163 dependencies[eval_sources_index + i] = eval_untracked_sources[i]; 146 164 } 147 165 } else { 148 - // there isn't any existing dependencies, so just replace the existing 149 - // array with the new one. 166 + // no old dependencies are active, so we can just replace the array. 150 167 dependencies = eval_listener!._dependencies = eval_untracked_sources; 151 168 } 152 169 153 - // now we subscribe to the new dependencies, but only if we're currently 154 - // configured as tracking. 170 + // subscribe to the new dependencies, but only if we're tracking. 155 171 if (eval_listener!._flags & Flags.TRACKING) { 156 172 for (let i = eval_sources_index, ilen = dependencies.length; i < ilen; i++) { 157 173 const dep = dependencies[i]; ··· 160 176 } 161 177 } 162 178 } else if (eval_sources_index < eval_listener!._dependencies.length) { 163 - // we don't have new dependencies, but the index pointer isn't pointing to 164 - // the end of the array, so we need to clean up the rest. 179 + // no new dependencies were added, but we need to remove the stale ones. 165 180 prune_old_dependencies(); 166 181 dependencies.length = eval_sources_index; 167 182 } 168 183 } 169 184 185 + /** 186 + * unsubscribes from stale dependencies. 187 + */ 170 188 function prune_old_dependencies(): void { 171 189 const dependencies = eval_listener!._dependencies; 172 190 ··· 176 194 } 177 195 } 178 196 197 + /** 198 + * runs the cleanup functions of an effect. 199 + * @param effect the effect to cleanup 200 + */ 179 201 function cleanup_effect(effect: Effect<any>): void { 180 202 const cleanups = effect._cleanups; 181 203 ··· 208 230 } 209 231 } 210 232 233 + /** 234 + * disposes an effect. 235 + * @param effect the effect to dispose 236 + * @param run_cleanup whether to run the cleanup functions 237 + */ 211 238 function dispose_effect(effect: Effect<any>, run_cleanup: boolean): void { 212 239 const dependencies = effect._dependencies; 213 240 ··· 297 324 * retrieves the value of the signal and creates a dependency if inside a computation. 298 325 */ 299 326 get value(): T { 300 - // check if we're running under a context 327 + // if we're in a computation, we need to track this signal as a dependency. 301 328 if (eval_listener !== undefined && eval_listener._context_epoch !== this._access_epoch) { 302 - // store the read epoch, we don't need to recheck the dependencies of 303 - // this effect. 329 + // we've already seen this signal, so we don't need to track it again. 304 330 this._access_epoch = eval_listener._context_epoch; 305 331 306 - // dependency tracking is simple: does the index pointer point to us? 307 - // 308 - // - if so, then we're already depended on and we can increment the 309 - // pointer for the next signal. 310 - // 311 - // - if not, then we need to create a new dependency array and stop 312 - // incrementing the pointer, now that pointer acts as a dividing line 313 - // between signals that are still being depended, and are no longer 314 - // depended upon. the new dependencies can be concatenated afterwards. 315 - 332 + // if the pointer is pointing to us, we're already a dependency. 333 + // otherwise, we need to add ourselves to the new dependencies. 316 334 if (eval_untracked_sources !== undefined) { 317 335 eval_untracked_sources.push(this); 318 336 } else if (eval_listener._dependencies[eval_sources_index] === this) {
+15
store.ts
··· 30 30 const get_descriptor = /*#__PURE__*/ Object.getOwnPropertyDescriptor; 31 31 const is_extensible = /*#__PURE__*/ Object.isExtensible; 32 32 33 + /** 34 + * the proxy handler for reactive objects. 35 + */ 33 36 const proxy_handler: ProxyHandler<any> = { 34 37 get(target, prop, receiver) { 35 38 if (prop === RAW_SYMBOL) { ··· 40 43 let metadata: ObjectMetadata = target[METADATA_SYMBOL]; 41 44 let s = metadata._values[prop]; 42 45 46 + // if we're in a computation and the property is not yet tracked, 47 + // we need to create a new signal for it. 43 48 if (s === undefined && eval_listener && (!(prop in target) || get_descriptor(target, prop)?.writable)) { 44 49 s = metadata._values[prop] = signal(target[prop]); 45 50 } ··· 180 185 }, 181 186 }; 182 187 188 + /** 189 + * checks if an object can be wrapped in a proxy. 190 + * @param obj the object to check 191 + * @returns `true` if the object is wrappable, `false` otherwise 192 + */ 183 193 function is_wrappable(obj: any): boolean { 184 194 const proto = get_prototype_of(obj); 185 195 return (proto === null || proto === object_proto || proto === array_proto) && is_extensible(obj); 186 196 } 187 197 198 + /** 199 + * initializes the metadata for a reactive object. 200 + * @param value the object to initialize 201 + * @returns the metadata object 202 + */ 188 203 function initialize(value: any): ObjectMetadata { 189 204 return { 190 205 _is_array: get_prototype_of(value) === array_proto,