public static IDisposable Reaction <T>( AtomPull <T> pull, Action <T> reaction, IEqualityComparer <T> comparer = null, bool fireImmediately = false, Action <Exception> exceptionHandler = null) { return(Reaction(pull, (value, _) => reaction(value), comparer, fireImmediately, exceptionHandler)); }
/// <summary> /// Creates an atom that compute its value by a function.<br/> /// </summary> /// <remarks> /// Computed values can be used to derive information from other atoms. /// They evaluate lazily, caching their output and only recomputing /// if one of the underlying atoms has changed. If they are not observed /// by anything, they suspend entirely.<br/> /// <br/> /// Conceptually, they are very similar to formulas in spreadsheets, /// and can't be underestimated. They help in reducing the amount of state /// you have to store and are highly optimized. Use them wherever possible. /// </remarks> /// <param name="pull">Function for pulling value.</param> /// <param name="keepAlive">Should an atom keep its value cached when there are no subscribers?</param> /// <param name="requiresReaction">Should an atom print warnings when its values are tried to be read outside of the reaction?</param> /// <param name="callbacks">Atom lifetime callbacks.</param> /// <param name="comparer">Value comparer used for reconciling.</param> /// <param name="debugName">Debug name for this atom.</param> /// <typeparam name="T">Atom value type.</typeparam> /// <returns>Created atom.</returns> /// <example> /// /// var a = Atom.Value(1); /// var b = Atom.Value(2); /// /// var sum = Atom.Computed(() => a.Value + b.Value); /// /// Debug.Log(sum.Value); /// /// </example> public static Atom <T> Computed <T>( AtomPull <T> pull, bool keepAlive = false, bool requiresReaction = false, IAtomCallbacks callbacks = null, IEqualityComparer <T> comparer = null, string debugName = null) { return(new ComputedAtom <T>(debugName, pull, null, keepAlive, requiresReaction, callbacks, comparer)); }
public static Atom <T> Computed <T>( AtomPull <T> pull, bool keepAlive = false, bool requiresReaction = false, Action onActive = null, Action onInactive = null, IEqualityComparer <T> comparer = null) { return(new ComputedAtom <T>(pull, null, keepAlive, requiresReaction, onActive, onInactive, comparer)); }
internal ComputedAtom( [NotNull] AtomPull <T> pull, AtomPush <T> push = null, bool keepAlive = false, bool requiresReaction = false, Action onActive = null, Action onInactive = null, IEqualityComparer <T> comparer = null) : base(keepAlive, onActive, onInactive) { _pull = pull ?? throw new ArgumentNullException(nameof(pull)); _push = push; _comparer = comparer ?? EqualityComparer <T> .Default; _requiresReaction = requiresReaction; }
internal ComputedAtom( string debugName, [NotNull] AtomPull <T> pull, AtomPush <T> push = null, bool keepAlive = false, bool requiresReaction = false, IAtomCallbacks callbacks = null, IEqualityComparer <T> comparer = null) : base(debugName, keepAlive, callbacks) { _pull = pull ?? throw new ArgumentNullException(nameof(pull)); _push = push; _comparer = comparer ?? EqualityComparer <T> .Default; _requiresReaction = requiresReaction; }
/// <summary> /// Creates an reaction that takes two functions: /// the first, data function, is tracked and returns the data that is used /// as input for the second, effect function. It is important to note that /// the side effect only reacts to data that was accessed in the data function, /// which might be less than the data that is actually used in the effect function. /// /// The typical pattern is that you produce the things you need in your side effect /// in the data function, and in that way control more precisely when the effect triggers. /// </summary> /// <param name="reaction">A data function.</param> /// <param name="effect">A side effect function.</param> /// <param name="exceptionHandler">A function that called when an exception is thrown while computing an reaction.</param> /// <param name="comparer">Data comparer used for reconciling.</param> /// <param name="fireImmediately">Should the side effect runs once when you create a reaction itself? Default value is true.</param> /// <param name="debugName">Debug name for this reaction.</param> /// <typeparam name="T">Reaction data type.</typeparam> /// <returns>Created reaction.</returns> /// <example> /// /// var counter = Atom.Value(1); /// /// var reaction = Atom.Reaction( /// () => counter.Value, /// val => Debug.Log("Counter: " + val) /// ); /// /// </example> public static Reaction Reaction <T>( AtomPull <T> reaction, Action <T> effect, Action <Exception> exceptionHandler = null, IEqualityComparer <T> comparer = null, bool fireImmediately = true, string debugName = null) { return(Reaction( reaction: reaction, effect: (value, _) => effect(value), exceptionHandler: exceptionHandler, comparer: comparer, fireImmediately: fireImmediately, debugName: debugName )); }
/// <summary> /// Creates an reaction that takes two functions: /// the first, data function, is tracked and returns the data that is used /// as input for the second, effect function. It is important to note that /// the side effect only reacts to data that was accessed in the data function, /// which might be less than the data that is actually used in the effect function. /// /// The typical pattern is that you produce the things you need in your side effect /// in the data function, and in that way control more precisely when the effect triggers. /// </summary> /// <param name="reaction">A data function.</param> /// <param name="effect">A side effect function.</param> /// <param name="exceptionHandler">A function that called when an exception is thrown while computing an reaction.</param> /// <param name="comparer">Data comparer used for reconciling.</param> /// <param name="fireImmediately">Should the side effect runs once when you create a reaction itself? Default value is true.</param> /// <param name="debugName">Debug name for this reaction.</param> /// <typeparam name="T">Reaction data type.</typeparam> /// <returns>Created reaction.</returns> /// <example> /// /// var counter = Atom.Value(1); /// /// var reaction = Atom.Reaction( /// () => counter.Value, /// (val, reactionHandle) => /// { /// Debug.Log("Counter: " + val); /// if (val == 10) /// { /// reactionHandle.Deactivate(); /// } /// } /// ); /// /// </example> public static Reaction Reaction <T>( AtomPull <T> reaction, Action <T, Reaction> effect, Action <Exception> exceptionHandler = null, IEqualityComparer <T> comparer = null, bool fireImmediately = true, string debugName = null) { var valueAtom = Computed(reaction, comparer: comparer); bool firstRun = true; Reaction atom = null; atom = new ReactionAtom(debugName, () => { var value = valueAtom.Value; using (NoWatch) { if (firstRun) { firstRun = false; if (!fireImmediately) { return; } } // ReSharper disable once AccessToModifiedClosure effect(value, atom); } }, exceptionHandler); atom.Activate(); return(atom); }
public static IDisposable Reaction <T>( AtomPull <T> pull, Action <T, IDisposable> reaction, IEqualityComparer <T> comparer = null, bool fireImmediately = false, Action <Exception> exceptionHandler = null) { var valueAtom = Computed(pull, comparer: comparer); bool firstRun = true; ReactionAtom atom = null; atom = new ReactionAtom(() => { var value = valueAtom.Value; using (NoWatch) { if (firstRun) { firstRun = false; if (!fireImmediately) { return; } } // ReSharper disable once AccessToModifiedClosure reaction(value, atom); } }, exceptionHandler); atom.Get(); return(atom); }
public static ComputedAtom <T> Create <T>(string debugName, AtomPull <T> pull) { return(new ComputedAtom <T>(debugName, pull)); }