Skip to content

Releases: stackblitz/alien-signals

v2.0.0

30 Apr 12:54
Compare
Choose a tag to compare

Version 2.0 rethinks and optimizes the reactive system model, with minimal impact on users who only use the surface APIs. If you extend or deeply utilize alien-signals, please pay attention to the "Reactive Model Refactor" section.

Changes to Surface APIs

  • Added four new APIs: getCurrentSub, setCurrentSub, getCurrentScope, setCurrentScope

Deferred Signal Value Evaluation

Differences from v1

In v1, assigning a value to a signal immediately propagated the Dirty flag, causing some computed values to be unnecessarily re-evaluated.

// v1
const src = signal(10);
const double = computed(() => src() * 2);

double(); // -> 20

src(999); // double.flags -> Dirty
src(10);  // no effect

double(); // -> 20 (recomputed unnecessarily)

In v2, assigning a value to a signal only propagates the Pending flag, reducing unnecessary recomputation. Actual evaluation occurs during the next read.

// v2
const src = signal(10);
const double = computed(() => src() * 2);

double(); // -> 20

src(999); // src.flags -> Dirty, double.flags -> Pending
src(10);  // no effect

double(); // Checks src state -> unchanged, no recomputation needed

Effect Scope Parent-Child Hierarchy

In v2, recursive cleanup is achieved through a parent-child structure:

const scope1 = effectScope(() => {
	const scope2 = effectScope(() => {
		effect(() => ...);
		computed(() => ...);
	});
});

scope1();

Calling scope1() automatically cleans up its child scope scope2.

To make scope2 independent of scope1, temporarily set activeScope = undefined manually:

const scope1 = effectScope(() => {
	const prev = setCurrentScope(undefined);
	const scope2 = effectScope(() => {
		effect(() => ...);
		computed(() => ...);
	});
	setCurrentScope(prev);
});

Reactive Model Refactor

  • Merged Subscriber and Dependency into ReactiveNode
  • Trigger unwatched(dep) when all subscribers are lost, without recursively clearing subsequent subscribers
  • propagate now only propagates Pending; to immediately mark Dirty, call propagate + shallowPropagate after assignment
  • Adjusted naming for EffectFlags and ReactiveFlags; removed unused flags

Updated Options and APIs

  • notifyEffect β†’ notify
  • updateComputed β†’ update
  • Added the unwatched option for custom handling when all subscribers are lost
  • Removed processEffectNotifications, processComputedUpdate, processPendingInnerEffects, updateDirtyFlag
  • Added unlink, checkDirty

For performance differences, please refer to js-reactivity-benchmark.

Contributors

Thanks~

Special Sponsor

Next Generation Tooling

Platinum Sponsors

An approachable, performant and versatile framework for building web user interfaces.

Stay in the flow with instant dev experiences.
No more hours stashing/pulling/installing locally

β€” just click, and start coding.

Essential tools for software developers and teams.

Silver Sponsors

You?

v1.0.0

14 Jan 13:58
Compare
Choose a tag to compare

New createReactiveSystem() API

The core algorithm is now decoupled from the public API, and you can redesign your surface API using the alien-signals algorithm.

(The following example is based on proposal-signals/signal-polyfill#44)

const system = createReactiveSystem({
  updateComputed(computed: Computed) {
    return computed.update();
  },
  notifyEffect(watcher: subtle.Watcher) {
    if (watcher.flags & alien.SubscriberFlags.Dirty) {
      watcher.run();
      return true;
    }
    return false;
  },
});

signal, computed, effect, effectScope are now function-based instead of class-based

The class-based API design can minimize memory usage, but at the same time I think the get() and set() methods of class signal are also significantly worse in DX.

Since the core algorithm is now decoupled from the surface API, we decided to stop caring about scalability and memory footprint and switch to a function-based API to ensure proper DX by default.

For the latest API usage, please refer to the readme: https://github.com/stackblitz/alien-signals#usage

Performance improved by 12% compared to v0.6

With the 1.0 refactor changes, we have again seen performance improvements in our internal benchmark scripts in alien-signals.

Please note that this improvement is only theoretical since the --jitless flag is disabled in the alien-signals bench script, but it is a good thing anyway. :)

image