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!: `onCleanup` function

Mary 72eb2324 4da45038

+43 -21
+43 -21
mod.ts
··· 12 12 13 13 /** @internal currently evaluating listener */ 14 14 export let eval_listener: Computation | undefined; 15 + /** @internal currently evaluating effect */ 16 + export let eval_effect: Effect<any> | undefined; 15 17 16 18 /** pointer for checking existing dependencies in a context */ 17 19 let eval_sources_index: number = 0; ··· 162 164 } 163 165 164 166 function cleanup_effect(effect: Effect<any>): void { 165 - const cleanup = effect._cleanup; 167 + const cleanups = effect._cleanups; 166 168 167 - if (cleanup !== undefined) { 169 + if (cleanups !== undefined) { 168 170 const prev_listener = eval_listener; 171 + const prev_effect = eval_effect; 169 172 170 - effect._cleanup = undefined; 173 + effect._cleanups = undefined; 171 174 172 175 /*#__INLINE__*/ start_batch(); 173 176 eval_listener = undefined; 177 + eval_effect = undefined; 174 178 175 179 try { 176 - cleanup(); 180 + for (let i = 0; i < cleanups.length; i++) { 181 + (0, cleanups[i])(); 182 + } 177 183 } catch (err) { 178 184 // Failed to clean, so let's dispose of this effect. 179 185 effect._flags = (effect._flags & ~Flags.RUNNING) | Flags.DISPOSED; ··· 182 188 throw err; 183 189 } finally { 184 190 eval_listener = prev_listener; 191 + eval_effect = prev_effect; 185 192 186 193 end_batch(); 187 194 } ··· 348 355 } 349 356 350 357 /** @internal */ 351 - _refresh(): boolean { 358 + override _refresh(): boolean { 352 359 // Retrieve the current flags, make sure to unset NOTIFIED now that we're 353 360 // running this refresh. 354 361 const flags = (this._flags &= ~Flags.NOTIFIED); ··· 416 423 } 417 424 418 425 /** @internal */ 419 - _subscribe(target: Computation): void { 426 + override _subscribe(target: Computation): void { 420 427 // Subscribe to our sources now that we have someone subscribing on us 421 428 if (this._dependants.length < 1) { 422 429 const dependencies = this._dependencies; ··· 432 439 } 433 440 434 441 /** @internal */ 435 - _unsubscribe(target: Computation): void { 442 + override _unsubscribe(target: Computation): void { 436 443 super._unsubscribe(target); 437 444 438 445 // Unsubscribe from our sources since there's no one subscribing to us ··· 466 473 /** 467 474 * Retrieves the value without being tracked as a dependant 468 475 */ 469 - peek(): T { 476 + override peek(): T { 470 477 this._refresh(); 471 478 472 479 if (this._flags & Flags.HAS_ERROR) { ··· 479 486 /** 480 487 * Retrieves the value, tracks the currently-running effect as a dependant 481 488 */ 482 - get value(): T { 489 + override get value(): T { 483 490 this._refresh(); 484 491 485 492 if (this._flags & Flags.HAS_ERROR) { ··· 501 508 /** @internal Context flags */ 502 509 _flags = Flags.TRACKING; 503 510 504 - /** @internal Registered cleanup function */ 505 - _cleanup?: () => void; 511 + /** @internal Registered cleanup functions */ 512 + _cleanups?: (() => void)[]; 506 513 /** @internal Compute function for this effect */ 507 514 _compute: () => unknown; 508 515 /** @internal Stored value from this compute */ ··· 520 527 const flags = this._flags; 521 528 522 529 const prev_listener = eval_listener; 530 + const prev_effect = eval_effect; 523 531 const prev_sources = eval_untracked_sources; 524 532 const prev_sources_index = eval_sources_index; 525 533 ··· 532 540 /*#__INLINE__*/ start_batch(); 533 541 534 542 eval_listener = this; 543 + eval_effect = this; 535 544 eval_untracked_sources = undefined; 536 545 eval_sources_index = 0; 537 546 ··· 543 552 finalize_dependencies(); 544 553 545 554 eval_listener = prev_listener; 555 + eval_effect = prev_effect; 546 556 eval_untracked_sources = prev_sources; 547 557 eval_sources_index = prev_sources_index; 548 558 ··· 563 573 } 564 574 565 575 try { 566 - const ret = this._compute(); 567 - 568 - if (typeof ret === 'function') { 569 - this._cleanup = ret as (() => void); 570 - } 576 + this._compute(); 571 577 } finally { 572 578 finish(); 573 579 } ··· 628 634 /** 629 635 * Run side-effects that get rerun when one of its signal dependencies change. 630 636 */ 631 - export function effect( 632 - fn: () => unknown, 633 - ): () => void { 637 + export function effect(fn: () => unknown): () => void { 634 638 const instance = new Effect(fn); 639 + const dispose = instance._dispose.bind(instance); 635 640 636 641 try { 637 642 instance._refresh(); 638 643 } catch (err) { 639 - instance._dispose(); 644 + dispose(); 640 645 throw err; 641 646 } 642 647 643 - return instance._dispose.bind(instance); 648 + return dispose; 649 + } 650 + 651 + /** 652 + * register a cleanup function that is called when the effect is disposed 653 + * @param fn the cleanup function 654 + * @param failSilently whether to warn if called outside of an effect 655 + */ 656 + export function onCleanup(fn: () => void, failSilently = false): void { 657 + if (eval_effect) { 658 + if (eval_effect._cleanups === undefined) { 659 + eval_effect._cleanups = [fn]; 660 + } else { 661 + eval_effect._cleanups.push(fn); 662 + } 663 + } else if (!failSilently) { 664 + console.warn(`onCleanup was called outside of an effect`); 665 + } 644 666 } 645 667 646 668 /**