понедельник, 18 апреля 2022 г.

Stripe в стартапе: какой вариант интеграции выбрать?

Stripe (https://stripe.com/) - это не только платежная система со своим апи, но и готовые UI элементы и готовый web интерфейс для работы с платежами. Можно сделать удобный/красивый/функциональный платежный сервис прямо на своем сайте и на все платежные сценарии (включая refund).

суббота, 16 апреля 2022 г.

Любопытные грабли в JS: что вернет конструкция () => { name: 'my name' } ?

Мне было лень написать return и вокруг всего ставить еще одну пару фигурных скобочек, поэтому я решил сэкономить на них.

И получил 'null'.

Для меня это оказалось неожиданностью и я тщательно проотлаживал весь код вокруг этой функции в поисках ошибки или опечатки... И опечатка действительно есть, только она оказалась внутри этой анонимной функции: компилятор считает, что фигурные скобки открывают тело функции, внутри которой нет никакого 'return value'. Действительно должен быть null.

Что бы все таки получить из функции объект и при этом не писать явный return и новые фигурные скобки нужно написать так:

() => ({ name: 'my name' })

В новом варианте компилятор правильно решит, что мне лень написать фигурные скобки и 'return' и он сделает мой объект результатом выполнения функции.

понедельник, 27 декабря 2021 г.

Грабли 'setTimeout'

Исходная проблема: T1051228 - Form - In some circumstances, browser hangs up when resizing if there is a DxForm with column adaptability

Проблема возникла из-за особенностей кода в dxEventEngine: иногда код зацикливается. Вкратце "если при выполнении обработчика события на это же событие добавляется новый обработчик" + еще несколько условий. Код dxEventEngine решили не менять: слишком большая вероятность что-то сломать в совершенно неожиданных местах.

Решили использовать setTimeout в коде компонента dxForm: https://github.com/DevExpress/DevExtreme/pull/20688/files

Я начал изучать эту проблему, я передавал ее ответственным за dxEventEngine, я получил ее обратно, изучил расклад и согласен, что изменение кода dxForm будет иметь меньше эффектов, чем изменение кода eventEngine.

Ребята предложили такое элементарное изменение:
instance.on('autoColCountChanged', function() {
    setTimeout(() => {
        that._refresh();
    }, 0);
});
Это изменение и правда элементарно, но эффекты от него посложнее:
  1. Самый частый эффект "забыть вызвать clearTimeout перед setTimeout" и получить зацикливание, когда метод вызывают, он добавляет свой вызов в очередь "setTimeout", отрабатывает, получает управление из очереди "setTimeout" и снова начинает работать, goto 1.
    Для обработки этой ситуации нужен такой код:
             instance.on(
                'autoColCountChanged',
                () => {
                    if(this.autoColCountChangedTimeoutId) {
                        clearTimeout(this.autoColCountChangedTimeoutId);
                        this.autoColCountChangedTimeoutId = undefined;
                    }
                    this.autoColCountChangedTimeoutId = setTimeout(
                        () => this._refresh(),
                        0
                    );
                }
            );
  2. Второй эффект "забыть вызвать clearTimeout на dispose" и получить nullref, когда метод начнет работать с "дохлыми" свойствами "убитого" объекта, если после setTimeout но до начала выполнения элемента из очереди "setTimeout" был вызов dispose
    Для этой ситуации нужны еще несколько строчек кода:
        _dispose: function() {
            if(this.autoColCountChangedTimeoutId) {
                clearTimeout(this.autoColCountChangedTimeoutId);
                this.autoColCountChangedTimeoutId = undefined;
            }
            this.callBase();
        },
  3. Третий эффект "Breaking Change: изменение синхронного выполнения кода на выполнение когда-то потом через постановку в очередь", когда вызывающий код хочет обрабатывать результаты работы метода сразу после вызова метода: с вызовом setTimeout этих результатов не будет, ведь работа над ними еще не началась.
    В моем случае такого ожидания вроде бы нет, потому что есть только один вызов без обращений к результатам работы кода:
        _dimensionChanged: function() {
            if(this.option('colCount') === 'auto' && this.isCachedColCountObsolete()) {
                this._eventsStrategy.fireEvent('autoColCountChanged');
            }
        },
    Но про остальной код вокруг этого метода я конечно же ничего не гарантирую.
    Например, у меня сразу же упали тесты, которые как раз хотят получить результаты работы этого метода сразу же после вызова, и для них мне пришлось вписать emulatedTimer.tick(), что бы получить эти результаты.
    Аналогичная ситуация может возникнуть и в клиентских приложениях, если будет несколько обработчиков события "dimensionChanged" после dxForm._dimensionChanged и они ожидают готовые результаты работы метода "_refresh()"

  4. Четвертый эффект "БЧ: для управления выполнением кода можно применить setTimeout только один раз", когда кто-то уже использовал setTimeout в своем коде, что бы выполнить свой код после "dxForm._refresh" и получить результаты его работы. После такого изменения вызов dxForm._refresh произойдет позже и клиентский код не получит эти результаты.
    Тут я конечно же тоже ничего не гарантирую.

  5. Пятый эффект "невозможно написать тесты": я не понял как заставить наш eventEngine зациклиться при выполнении обработчиков, поэтому PR без тестов. Пишите, если у кого-то получилось написать такой тест.
Из-за этих эффектов я стараюсь не применять setTimeout для управления последовательностью выполнения кода: слишком много комбинаций.

Хотя иногда приходится делать решение именно на этом методе.

пятница, 22 октября 2021 г.

Зачем разрабочику линейки React компонентов использовать TypeScript?

Конечно же что бы его разработчики конечных приложений делали свои приложения быстрее!

Например, разработчик добавил в свою линейку компонентов новый простой компонент, который рисует текст и вызывает колбек для получения дополнительного текста:

function MyLabel({ text, onGetText } : any) {
    return
        <div>
            text1: { text}, 
            text2: { onGetText({ number: 42 }) }
        </div>;
}

Какие тесты мне совершенно не помогают?

Groups nested in the tabbed form item cause the "Too much recursion" error in FireFox Тесты, которые проверяют использование конкретного решения.

Например, в файле toolbarModule.tests.js из https://github.com/DevExpress/DevExtreme/pull/19205/files есть проверка assert.strictEqual($boxItemContent.css('flexBasis'), 'auto', 'Box item content flex-basis is \'auto\'');