Giter Club home page Giter Club logo

Comments (14)

Riim avatar Riim commented on July 19, 2024

Привет, в 1.6.58 добавил cellx.transact.
Мне кажется, что mobx.transaction немного неправильно работает:

let num = mobx.observable(1);
let num2 = mobx.observable(2);

try {
    mobx.transaction(() => {
        num.set(2);
        num.set(3);
        num2.set(5);
        throw 1;
        num2.set(10);
    });
} catch (err) {}

console.log('num: ' + num.get());
// => "num: 2"
console.log('num2: ' + num2.get());
// => "num2: 5"

Из википедии:

Транза́кция — минимальная логически осмысленная операция, которая имеет смысл и может быть совершена только полностью.

То есть mobx должен откатить изменения из-за ошибки и последние два console.log-а должны вывести "num: 1" и "num2: 2".

У меня вроде получились правильные транзакции:

let num = cellx(1);
let num2 = cellx(2);

cellx.transact(() => {
    num(2);
    num(3);
    num2(5);
    throw 1;
    num2(10);
});

console.log('num: ' + num());
// => num: 1
console.log('num2: ' + num2());
// => num2: 2

отладка

порядок вычисления в синхронном режиме может заметно измениться и такая отладка скорее оставит ещё больше вопросов.

обработка ошибок

это актуально для mobx который падает при ошибке в формуле, cellx в этом случае создаёт на ячейке событие error, оно же распространяется по всем вычисляемым ячейкам. Единственный способ добиться падения -- запись в невычисляемую ячейку, но тут и до вычисления просто не доходит. В общем, совсем другой подход к обработке ошибок.

Если всё равно очень нужно, то можно включать костылём вроде такого (не проверял):

let originalSet = cellx.Cell.prototype.set;
cellx.Cell.prototype.set = function() {
    originalSet.apply(this, arguments);
    cellx.Cell.forceRelease();
};

но добавлять это в библиотеку как-то не хочется, так как костылище)).

from cellx.

zerkalica avatar zerkalica commented on July 19, 2024

Спасибо.

порядок вычисления в синхронном режиме может заметно измениться и такая отладка скорее оставит ещё больше вопросов.

Не очень понимаю о чем речь, пример можно?

from cellx.

Riim avatar Riim commented on July 19, 2024

http://jsbin.com/cudevigobe/edit?js,console
асинхронно: "c", "d"
синхронно: "d", "c", "d".

from cellx.

zerkalica avatar zerkalica commented on July 19, 2024

Несколько set нужно делать только внутри транзакции. Транзакции через таймеры, которые сейчас, хоть и просто, но это магия, некое соглашение, поведение которого нельзя изменить. В коде лучше явно сказать, что эти апдейты внутри транзакции, чем держать в голове, что они выполнятся в следующем тике.

Как быть с известной реактовой багой, когда курсор перескакивает в начало инпута, если стейт обновился асинхронно. Скорее forceRelease (как и любой другой force*) выглядит костылем, чем синхронное обновление+транзакции.

По поводу обработки ошибок: http://jsbin.com/kesiperese/1/edit?js,console
Если откомментарить // cellx.Cell.forceRelease();, то виден весь путь до ошибки, а без него, только до cellx.

В баобабе, например, тоже изначально только через таймеры были транзакции, потом автор добавил параметр asynchronous, по-умолчанию он true, но скорее всего это из-за совместимости с легаси.

from cellx.

Riim avatar Riim commented on July 19, 2024

В 1.6.60 синхронный режим можно включить так:

cellx.configure({ asynchronous: false });

from cellx.

zerkalica avatar zerkalica commented on July 19, 2024

Спасибо, работает.

Баг только обнаружил:

let a = cellx(1);
cellx.configure({ asynchronous: false });

function fn(err, evt) {
    console.log(evt.value)
}

console.log('start')

var o = {a: a}
o.a('subscribe', fn)
a(2) // ничего не выведет

cellx считает o.a() и a() как разные ячейки

http://jsbin.com/hobikigeju/edit?js,console

from cellx.

Riim avatar Riim commented on July 19, 2024

Это фича)) Значение ячейки привязано к контексту в котором она запускается, это необходимо для возможности объявления ячеек в прототипе:

function User() {}
User.prototype.name = cellx();

var user1 = new User();
var user2 = new User();

user1.name('Матроскин');
user2.name('Шарик');

console.log(user1.name());
// => "Матроскин"

Без этой фичи user1.name() вдруг станет Шариком.
Если это не нужно, можно использовать внутренний класс cellx.Cell, сам cellx -- это просто обёртка над ним, реализующая всякие такие странности с контекстом.

function User() {}
User.prototype.name = new cellx.Cell();

var user1 = new User();
var user2 = new User();

user1.name.set('Матроскин');
user2.name.set('Шарик');

console.log(user1.name.get());
// => "Шарик"

from cellx.

zerkalica avatar zerkalica commented on July 19, 2024

Ок, не совсем очевидно как-то. Действительно так нужны эти упрощения в виде одной god-функции в ущерб явности? Может хотя бы какой-нить cellx.prop сделать ?

В mobx более логично выглядит extendsObservable(this, {bla-bla}), да, в конструкторе, но код понятнее получается.

На мой взгляд, проблема на стыке атомов и обычного кода, в mobx magic get/set, в cellx свойства похожиме на get/set методы. Из-за этого куча извратов городить приходится.

Например, в погоне за похожестью на привычный многим код, без оберток над данными, у тебя используется антипаттерн строковая типизация. Например, a('subscribe', fn) - по сути вызов метода a.subscribe(fn)

Если взять flow или typescript и по-честному, без any, переписать тип ICellx, то ни typescript, ни flow его не переварят, т.к. там нет паттерн-матчинга по сигнатуре функции.

Может конечно не так что-то делаю, вот пример на typescript:

type CellxEvent<V> = {
    type: 'change';
    oldValue?: V;
    value: V;
}
type ListenerName = 'addChangeListener' | 'removeChangeListener'
type CellxListener<V> = (eventName: ListenerName, fn: (event: CellxEvent<V>) => void) => void
type CellxSet<V> = (v: V) => void
type CellxGet<V> = () => V

type CellxAtom<V> = CellxListener<V> | CellxSet<V> | CellxGet<V>

type Cellx<V> = () => CellxAtom<V>

var f: any = 0
var fn: Cellx<number> = f

var a: CellxAtom<number> = fn()
var val: number = a() // Can not invoke an expression ...

По мне, идея atom-подобных оберток внутри обычного кода - костыль. Нужно наоборот, при помощи инверсии контроля
выполнять обычный код внутри атомов, т.е. di-контейнер управляет не только созданием объектов, но и их реактивностью, вынося за сцену все эти get/set, computable, observable, action.

from cellx.

Riim avatar Riim commented on July 19, 2024

так нужны эти упрощения в виде одной god-функции в ущерб явности

современные js-движки развращают своей скоростью, раньше создавать методы в конструкторе было страшным грехом, а сейчас пожалуйста, сколько хочешь. Объявление ячейки в прототипе позволяет не создавать функцию на каждое свойство каждого экземпляра, плюс экземпляры cellx.Cell создаются при этом лениво, при первом обращении к свойству. Это лучшее сочетание скорости и удобства.
Если же скорость и потребление памяти не особо важны, то есть cellx.define:

function User(name) {
    cellx.define(this, { name: name });
}

var user1 = new User();
var user2 = new User();

user1.name = 'Матроскин';
user2.name = 'Шарик';

console.log(user1.name);
// => "Матроскин"

Ещё более современный вариант: cellx-decorators.

Про странный синтаксис вызова методов рассказывал здесь: #8 . К написанному там можно добавить, что с новым, уже описанным в стандарте __proto__, подставить свой прототип функции всё же можно (IE11+):

function f() {}
f.__proto__ = { __proto__: Function.prototype, myProp: 1 };

console.log(f.call);
// => function call() { [native code] }
console.log(f.myProp);
// => 1

То есть при желании можно добиться и a.subscribe(fn) без переписывания методов в цикле как в том же KnockoutJS. Тут нужно будет поисследовать насколько это быстрее/медленнее. Сейчас я просто давно уже перешёл на декораторы и cellx.define, так что уже не заморачиваюсь на эту тему)).

С тайпскриптом давно не работаю, раньше использовал эту типизацию: https://github.com/Riim/cellx/blob/master/src/cellx.d.ts . Стараюсь обновлять её стабильным функционалом, вроде должна быть рабочей.

from cellx.

zerkalica avatar zerkalica commented on July 19, 2024

по поводу #8, ну да, не берусь тут судить, производительность наверняка выше и памяти поменьше ест, если контекст не передавать ссылкой, а this использовать.

Просто выглядит не очень очевидно с контекстом, но это из-за решения проблемы "как сделать код с атомами похожий на код без атомов с минимальным оверхедом".

Про интерфейс, формально он есть и рабочий, но на мой взгляд больше для галочки. Я про интерфейс именно фасада cellx, сам класс Cell то норм.

interface Cell<T> {
        get(): T;
...
}

interface ICellx<T> {
        (value?: T): T;

        (
            method: string,
            arg1: any,
            arg2?: any,
            arg3?: any,
            arg4?: any,
            arg5?: any
        ): ICellx<T> | Error | Promise<any> | Cell<T>;
    }

var a: ICellx<number>
a('test', 'some').get() // Property get does not exists...

from cellx.

Riim avatar Riim commented on July 19, 2024

Поправил типизацию:

cellx/src/cellx.d.ts

Lines 231 to 250 in 01457e6

(method: 'bind', zeroArg: any): ICellx<T>;
(method: 'unwrap', zeroArg: any): Cell<T>;
(method: 'addChangeListener', listener: IEventEmitterListener, context?: any): Cell<T>;
(method: 'removeChangeListener', listener: IEventEmitterListener, context?: any): Cell<T>;
(method: 'addErrorListener', listener: IEventEmitterListener, context?: any): Cell<T>;
(method: 'removeErrorListener', listener: IEventEmitterListener, context?: any): Cell<T>;
(method: 'subscribe', listener: (err: Error | void, evt: ICellEvent) => boolean | void, context?: any): Cell<T>;
(method: 'unsubscribe', listener: (err: Error | void, evt: ICellEvent) => boolean | void, context?: any):
Cell<T>;
(method: 'pull', zeroArg: any): boolean;
(method: 'getError', zeroArg: any): Error;
(method: 'push', value: any): Cell<T>;
(method: 'fail', err: any): Cell<T>;
(method: 'isPending', zeroArg: any): boolean;
(method: 'then', onFulfilled: (value: T) => any, onRejected?: (err: any) => any): Promise<any>;
(method: 'catch', onRejected: (err: any) => any): Promise<any>;
(method: 'dispose', zeroArg: any): Cell<T>;
.

from cellx.

zerkalica avatar zerkalica commented on July 19, 2024

о, теперь понятно, спасибо.

Немного отвлеченный вопрос, а не мог бы ты описать трюки, которые позволяют увеличить производительность и снизить потребления памяти. Примерно как Optimization-killers у petkaantonov.

Есть общеизвестные - не использовать генерацию функций, миксины вместо new Class, но может в твоем опыте что-то еще есть?

from cellx.

Riim avatar Riim commented on July 19, 2024

Вряд ли мой опыт заметно отличается от того, что можно найти в Интернете, но я подумаю)).

from cellx.

zerkalica avatar zerkalica commented on July 19, 2024

Т.к. cellx.transact добавлен и моя хотелка выполнена, тикет закрою.

from cellx.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.