Created
June 8, 2022 15:32
-
-
Save intrnl/9fac9ce36f77bc72f042e03cffb06dac to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** @type {?Scope} */ | |
let curr_scope = null; | |
/** | |
* @param {boolean} [detached] | |
* @returns {Scope} | |
*/ | |
export function scope (detached) { | |
let instance = new Scope(detached); | |
return instance; | |
} | |
/** | |
* @param {number} dependencies | |
* @param {() => void} runner | |
*/ | |
export function effect (dependencies, runner) { | |
runner(); | |
if (dependencies && curr_scope) { | |
curr_scope._track(dependencies, runner); | |
} | |
} | |
class Scope { | |
/** @type {?boolean} */ | |
_disabled = false; | |
/** @type {number} */ | |
_dependencies = 0; | |
/** @type {[dependencies: number, effect: (() => void)][]} */ | |
_effects = []; | |
/** @type {(() => void)[]} */ | |
_cleanups = []; | |
/** @type {Scope[]} */ | |
_scopes = []; | |
/** @type {?Scope} */ | |
_parent; | |
/** @type {?number} */ | |
_parent_idx; | |
/** | |
* @param {boolean} [detached] | |
*/ | |
constructor (detached) { | |
let instance = this; | |
if (!detached && curr_scope) { | |
instance._parent = curr_scope; | |
instance._parent_idx = curr_scope._scopes.push(instance) - 1; | |
} | |
} | |
/** | |
* @param {number} dirty | |
*/ | |
_invalidate (dirty) { | |
let instance = this; | |
let scopes = instance._scopes; | |
if (instance._dependencies & dirty) { | |
for (let [dependencies, runner] of instance._effects) { | |
if (dependencies & dirty) { | |
runner(); | |
} | |
} | |
} | |
if (scopes.length) { | |
for (let scope of scopes) { | |
scope._invalidate(dirty); | |
} | |
} | |
} | |
/** | |
* @param {number} dependencies | |
* @param {() => void} runner | |
*/ | |
_track (dependencies, runner) { | |
let instance = this; | |
instance._effects.push([dependencies, runner]); | |
instance._dependencies |= dependencies; | |
} | |
/** | |
* @template T | |
* @param {() => T} fn | |
* @returns {T} | |
*/ | |
_run (fn) { | |
let instance = this; | |
if (instance._disabled) { | |
return; | |
} | |
let prev_scope = curr_scope; | |
try { | |
curr_scope = instance; | |
return fn(); | |
} | |
finally { | |
curr_scope = prev_scope; | |
} | |
} | |
/** | |
* @param {boolean} [from_parent] | |
*/ | |
_stop (from_parent) { | |
let instance = this; | |
let parent = !from_parent && instance._parent; | |
instance._disabled = true; | |
instance._clear(); | |
if (parent) { | |
let last = parent._scopes.pop(); | |
let idx = instance._parent_idx; | |
if (last && last !== instance) { | |
parent._scopes[idx] = last; | |
last._parent_idx = idx; | |
} | |
} | |
} | |
_clear () { | |
let instance = this; | |
let effects = instance._effects; | |
let cleanups = instance._cleanups; | |
let scopes = instance._scopes; | |
for (let cleanup of cleanups) { | |
cleanup(); | |
} | |
for (let scope of scopes) { | |
scope._stop(true); | |
} | |
effects.length = 0; | |
cleanups.length = 0; | |
// scopes.length = 0; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment