Pull to refresh
18
0

User

Send message

Вы считаете что постоянно держать соединение дорого? Соединение в linux и в node это всего лишь файловый дескриптор размером с сотню байт. Расходов кроме памяти практически нет (браузеры либо вообще не отсылают специальные "ping"-вебсокет сообщения либо делают это очень редко — за полчаса я не получил ни одного такого сообщения, а потом мне ждать надоело)


А что касается http — в нем есть заголовок "connection: keep-alive" который сообщает серверу открыть и держать tcp-соединение (и пересылать все http запросы по этому соединению) точно так же как и с вебсокетами, плюс в версии http 1.1 это подразумевается по умолчанию (если явно не передано "connection: close"). В общем можно сказать что значительная часть интернета использует keep-alive. В таком случае у http нет никаких преимуществ перед websockets


Даже наоборот — в http есть фундаментальный недостаток — из-за того что http параллельный и "stateless" — браузеры не гарантируют что запросы на сервер поступят в том же порядке в котором были отправлены на клиенте (могут использовать keep-alive а могут и не использовать — это всего лишь оптимизация и узнать и проконтролировать со стороны javascript невозможно) и из-за этого на порядки усложняется решение одной из самых главных проблем всех бэкендов — https://habr.com/ru/company/yandex/blog/442762


А вот с вебсокетами проблема неидемпотентных запросов описанная в статье решается очень просто. Поскольку используется только одно единственное соединение которое сами контролируем то получаем гарантию что запросы на сервер будут поступать последовательно а значит на сервере достаточно сохранить только айдишник последнего запроса а не хранить айдишники для всех http-запросов (а как-то очищать по таймеру ненадежно потому потому что могут быть ситуации остановки приложений и запуска через время когда они уже были очищены)

А REST API обязательное условие? Что если разработчик в проектах использует другую технологию для взаимодействия клиента с сервером? Я например против подходов rest api и http в целом и считаю вебсокеты более эффективным способом коммуникации клиента и сервера (как минимум вебсокеты на порядок проще решают проблему идемпотетности — https://habr.com/ru/company/yandex/blog/442762) не говоря уже про реалтайм

В React Native только JIT-компиляция

Должен сказать что React Native не использует jit на iOS. Вот пруф (https://reactnative.dev/docs/javascript-environment)


Note that on iOS, JavaScriptCore does not use JIT due to the absence of writable executable memory in iOS apps.

То есть это значит что на iOS реактовский diff большого дерева компонентов будет тормозить потому что js будет интерпретироваться а не компилироваться в более оптимизированную версию как на android. Даже cordova будет быстрее потому что она использует webview со встроенным safari-движком а это единственное приложение которому iOS разрешает jit, соотвественно реакт c кордовой будет быстрее чем с react native

Я конечно понимаю что статья про rest но хочу напомнить что http это не единственный способ организации взаимодействия, есть еще вебсокеты. Я вот например выбросил http и перевел все взаимодействие клиента с сервером на вебсокеты и не нарадуюсь — мне стали не нужны все эти бэкенд-фреймворки (например expres, koa, nestjs) и библиотеки которые в основном нацелены на http-стек.
К тому же напомню что в области разработки десктопных приложений для взаимодействия клиента с сервером испокон веков использовали обычные сокеты. И только с появлением веба и потому что до некоторого времени в браузерах javascript поддерживал лишь отправку http запросов стали популярными все эти rest-подходы поверх http.
Но теперь когда у нас есть поддержка вебсокетов (а это не еще одна абстракция поверх http как бывают думают некоторые, да для установки соединения по вебсокетам используется http но дальше это просто передача хедера с размером сообщения поверх tcp) и растет популярность desktop-like web-приложений (а есть еще offline-first приложения которые могут работать без сети и нужно синхронизировать изменения) использовать http в качестве транспорта не имеет никакого смысла

Народ использовал классы раньше потому что тогда не было такого распространения компонентов и классы это был возможно единственный способ не дублировать стили при добавлении какой-нибудь кнопки в разных местах.
А теперь с появлением компонентного подхода какой смысл в классах? Нет больше той проблемы с дублированием зато есть неудобства в лишних переключениях между тегами и стилями и необходимость придумывать имена для классов.
Вопрос — если у меня есть уже компонент кпопки или какой-то карточки, зачем мне дополнительно создавать класс "button" или "card" и выносить стили в отдельный файл, или в отдельное место в том же файле? Или вот, например, когда мне нужно что-то изменить в дизайне. Я уже зашел в файл компонента <Card/> или <Button/> с целью изменить или поправить дизайн — зачем мне еще раз видеть на теге класс "card" и затем переходить в другое место для того чтобы увидеть стили? Почему бы просто не писать стили прямо рядом с тегами если мы уже разбиваем верстку на атомарные компоненты?

Способ такой. (Для его применения, надо обладать верной памятью). Надо провзаимодействовать с любым внешним объектом, хоть в чём либо превышающим ваши способности, и затем проверить результат. Например, калькулятор способен умножать числа намного быстрее, чем вы. Прекрасно! Воспользуйтесь калькулятором и запомните ответ, а затем проверьте его вручную — если ответ верен, то вы действительно можете воспользоваться внешним по отношению к вам объектом, а следовательно, не спите.

Интересно… сразу появляются вопросы:
1) А что уже доказано что мозг человека в состоянии сна не переключается например в режим суперспособностей способный переумножать числа не хуже калькулятора?
2) А может когда мозг во сне переумножает два каких-нибудь больших десятизначных числа (проверяя тот самый калькулятор) то это только видимость а на самом деле мозг усиленно пытается умножить два на два (а сам перед этим уже легко вычислил ответ когда играл роль калькулятора)

2) Проблему останова решать не нужно, если используется тайпскрипт или только подмножество js где мы можем за счет статического анализа найти циклы и рекурсию то дальше просто добавляем в цикл дополнительный код который будет проверять время и останавливать цикл если выполняется дольше положенного либо будет реагировать на кнопку остановки (если это внешний плагин в каком-то редакторе например как это работает в figma.com)
Известная компания Figma уже пыталась сделать песочницу используя всякие костыли вроде with и proxy-объекты — www.figma.com/blog/how-we-built-the-figma-plugin-system Но потом наевшись дыр они таки перешли на белый список правда через интерпретацию js собственным движком — www.figma.com/blog/an-update-on-plugin-security
Я придерживаюсь мнения что оба варианта это либо костыли либо тормоза. Правильным вариантом был бы статический анализ кода (тот же белый список но только без интерпретации — то есть со скоростью нативного js). Это правда реализовать сложнее — нужно взять некое подмножество typescript в котором невозможен будет any и будет разрешен только набор предусмотренных апишек но зато это будет белый список и эту песочницу по определению нельзя будет взломать
Интересно а как называется геометрия вселенной Симпсонов?
Как же много народу любят высказывать предположения о производительности (или даже не предположения а уверенность) не представляя реального положения вещей. Я предлагаю не гадать и вживую посмотреть в какой ассемблерный код компилируется switch движком v8 и соотвественно увидеть есть ли там хеши, jump-ы по таблице или это просто набор if-ов. Тем более это совсем не сложно. Запускаете в ноде с флагом node --print-opt-code --code-comments script.js и получите в консоли ассемблер в который компилируется js (только для этого функцию со switch-ем надо разогреть циклом либо обернуть глобальной функцией %OptimizeFunctionOnNextCall(fn) и запустить с флагом --allow-natives-syntax)
Очень многие моменты можно улучшить на уровне парсера просто убрав (или точнее не добавляя код который это парсит) ненужные фичи и конструкции js.
Вот например моменты неявного преобразования типов которые любят собирать чтобы продемонстрировать wtf-моменты языка связаны с операциями == и !=. Я же в своем парсере просто не стал парсить эти операции а разрешил только строгое сравнение === и !==. Также много ошибок связано с тем что строка складывается с не-строкой. Это тоже можно решить разрешив складывать строки только через template-literal — вместо a + b — юзаем `${a}${b}`. Да, для того чтобы полностью исключить эту ошибку нужен статический анализ но возможность складывания литерала строки с переменной уже можно запретить прямо в парсере.
И таких тонких моментов очень много. Много ли программистов полностью знают нюансы языка которые связаны с моментами того что можно написать в коде? В частности — то с какими символами юникода можно разделять токены (вы в курсе что кроме пробела и таба там может быть VT, FF, SP, NBSP, ZWNBSP, USP), какие символы будут приниматься за новую строку (кто знает про LS и PS ?) и что это вообще за юникод-символы ZWNJ, ZWJ, ZWNBSP которые играют роль контролов? (я уже молчу про RLO и RLE — habr.com/ru/post/252813 — это вообще ппц) и т.д.
А также сколько разработчиков js знают про нюансы того что можно написать в литерале строки (взгляните на www.ecma-international.org/ecma-262/10.0/index.html#sec-template-literal-lexical-components — меня просто бомбило когда я пытался с этим разобраться и понять зачем это нужно) И ведь без знания всех этих нюансов нельзя сказать что знаешь js потому что рано или поздно эти нюансы всплывут где-нибудь в коде в виде какого-то бага (или дыре в безопасности). И поэтому я и решил написать парсер и по белому списку начать парсить код и отбрасывать кучу фич и конструкций языка которые мне нафиг не сдались. Поддержка юникода в индефикаторах чтобы можно было писать в стиле 1с? — Нафиг. Только a-zA-z0-9. Куча способов разделить токены и строки? — Нафиг (только пробел и \n) Куча способов заэкскейпить символ юникода в строке? — Нафиг — только один единственный способ через \uXXXX. Куча способов написать переменную (var, const, let)? — Нафиг — только единственный способ через let. Куча способов объявить строку (одинарные, двойные, темплейт-литералы)? Нафиг. Куча конструкций чтобы объявить функцию (из-за которых возникают баги с hoisting — function declaration, function expression, arrow-function) — Нафиг. Куча фич и всяких неочевидных конструкций и моментов для работы с классами — … ну вы поняли… Я не юзаю классы и считаю их ненужными в языке (включая все эти фичи для работы с прототипами а также геттеры-сеттеры и всякие другие хаки) — только функция которая возвращает объект либо замыкания и соотвественно я не стал добавлять парсинг синтаксиса классов в свой парсер (ну и запретил ключевое слово this).
Тут любой может поспорить моими решениями и поэтому важно заметить что этот парсер-линтер я писал в первую очередь для себя чтобы вычертить по белому списку все мельчайшие нюансы языка чтобы быть уверенным в коде который я пишу ну и чтобы избавиться от этого ощущения незнания и груза на плечах о необходимости на протяжении дальнейшей карьеры постоянно подучивать какие-то неизвестные ранее нюансы языка. И как результат весь этот сложный javascript с кучей нюансов для меня упростился до небольшого и четкого перечня всех моментов которые мне нужны от языка где просто по определению не может быть неизвестных мне ранее нюансов
Я не мог понять, почему она такая глупая и простая, намного проще, чем любой API для удалённой работы, и почему компьютерные системы, кажется, не способны на такое.

Никто этого еще не придумал потому что задача по сложности эквивалентна синхронизации состояния в распределенных базах данных (а это просто огромная и сложная область да еще с кучей фундаментальных ограничений). Вот как вы собрались работать с шаред-состоянием в этих телепроцессах на разных машинах? У вас не то что там проблемы с многопоточностью как в обычных потоках — у вас появятся куда более серьезные проблемы консистентности из-за того что будет отсутствовать даже банальный атомарный бродкаст (который уже обеспечивается кеш-когерентностью самого процессора фундаментально упрощая проблемы многопоточного программирования). В итоге вам придется начинать все с нуля и алгоритмов вроде paxos-а или raft-а и погрузиться во все тяжкие распределенных систем и транзакционности.

Согласен. Двухстороннее связывание решает эту проблему (если правильно его спроектировать и запретить локальное состояние и оставить только это самое связывание и динамическое добавление полей) но на текущий момент у реакта, jsx или даже js есть фундаментальные ограничения которые не позволяют использовать эту фичу удобно и в полную силу (предполагаю поэтому и нужны шаблоны $mol потому что в жс описывать такие связи получится неудобно или нетипизируемо)


Вообще-то "двухстороннее связывание" вообще очень редкий зверь и похоже нигде кроме $mol его нет.
С первого взгляда даже может показаться что двухстороннее связывание это просто способ избежать болерплейта передачи вложенному компоненту вместе с переменной заодно и коллбека на ее изменение. Но если представить что мы во вложенном компоненте объявляем состояние только в том объекте который нам передал родительский компонент (а он в свою очередь храниться в свойстве объекта который был передан еще выше и т.д по цепочке) то в итоге получится что с одной стороны у нас как бы есть концепция "локального состояния" — любой компонент на любой глубине ui-дерева может объявить переменную состояния без необходимости декларировать где-то в другом месте (в родительском компоненте или например с глобальном сторе) а с другой стороны все это состояние на самом деле хранится в дереве единственного root-объекта и к состоянию любого глубокого компонента можно добраться извне или с любого компонента в любой другой части ui-иерархии

-данные о dirty, validation, touched и пр. и пр. в state

А вообще мне кажется redux надо запретить в первые годы использования React, чтобы он не травмировал представление о React и SPA в целом :D
Люди начинают мыслить очень узко и не видеть всей картинки. Всё пихать в стор, плодить мульёны файлов, а потом героически со всем эти сражаться, когда минорные фичи задевают 250 файлов в разных частях приложения.

А мне кажется совсем наоборот — это до тех пор пока связанность фич в проекте небольшая и не будет увеличиваться то можно позволить изолировать стейт в отдельных компонентах и не выносить в глобальный стор.
Но если появится какая-нибудь задачка, например — в совсем другой части приложения, на другой странице нужно при доступе к чему-то проверить что юзер заполнил форму и показать надпись, мол "друг, дополни эти поля которые ты начал заполнять и исправь такие-то поля которые невалидны" то всё, приехали — вам придется теперь выносить состояние полей — dirty, validation, touched которое раньше хранилось внутри одного компонента в общий стор потому что теперь эта информация нужна в совсем другой ui-ирерахии компонентов и соотвественно вместе с вынесением потребуется переписывать весь код работы с этим состоянием.
А вот если бы статусы полей формы изначально хранились в сторе то вам бы не пришлось бы переписывать уже существующие компоненты и фичи
А заказчику или продакт-менеджеру ведь не объяснишь что мол какая-то новая фича требует каких-то там непонятных архитектурных изменений — заказчик обычно думает о фичах в контексте того что можно делать любые проверки или отображать любую информацию в любой части приложения.
И я еще не видел ни одного проекта (который развивается и постоянно требует добавления новых фич) в котором бы решение изолировать какое-то состояние не приводило бы к проблемам — я вижу явную тенденцию что в любом развивающемся продукте количество фич и связность между ними будет только увеличиваться а значит решение изначально хранить любое состояние приложения в глобальном сторе более чем оправдано

Еще час я потратил, чтобы узнать, как кроссплатформенно открыть файл и узнать его размер. Кажется, никак, если нет С++17 c std::filesystem.

О, знакомая история… У меня опыта с плюсами и си совсем немного но даже этого хватило чтобы вызвать отвращение к возни с этими библиотеками (включая стандартную) и кроссплатформенностью (с кучей враперов со своими нюансами над другими враперами которые там где-то сводятся к системным вызовам). И я решил по-другому — раз мой софт все равно будет работать на сервере (или в докере) в контролируем окружении (только линукс, без тонны остального софта и сервисов всяких убунту-дебиан-дистрибутивов — то есть только само ядро с моей софтиной в виде статического бинарника в качестве pid0) — то в итоге без кроссплатформенности все стало намного проще — у меня остался один источник правды в виде системных вызовов линукса и я избавился от тучи нюансов стандартной библиотеки или других врапперов и легко написал нужную мне софтину работы с файлами и сокетами

Хорошая статья. Нечто подобное (но со стороны куда более простого языка) было и у меня. Некоторое время назад я считал что знаю javascript (как никак больше 6 лет на нем пишу) а потом открыл спеку и просто ужаснулся от того огромного количества нюансов, особенностей, моментов и разнообразных языковых конструкций про которые я до этого был не в курсе. И такое чувство что нужно не какие-то там десять-пятнадцать лет опыта — тут нужно быть разработчиком движка чтобы можно было сказать что знаешь js (хотя я не уверен что разработчики v8 знают наизусть все детали спеки). И знаете что помогло мне преодолеть этот страх от неизвестности и сбросить с плеч тот груз безысходности и необходимости всю остальную карьеру подучивать какие-то нюансы языка как какой-то там "вечный студент"? Я просто взял и написал свой парсер который парсит по белому списку лишь необходимое мне подмножество языка отбрасывая все те избыточные и переусложненные нюансами конструкции и фичи языка которые понапридумывали укуренные авторы спеки (и продолжают придумывать с каждым годом не считая то количество разнообразных пропозалов и stage0 фич). В итоге я не стал лучше разбираться в каких-то там нюансах языка — я просто перестал беспокоится о них так как в коде который я пишу эти нюансы и конструкции просто больше никогда мне не понадобятся и соотвественно скрытых нюансов просто быть не может так как мой парсер парсит все по белому списку нужных мне конструкций, нюансов синтаксиса и фич в которых я уверен

Я вот из тех людей которые твердо считают что первым языком должен быть ассемблер. Потому что именно ассемблер даст фундамент и понимание того как работает память, процессор и т.д — то есть те знания которые помогут отделить зерна от плевел при изучении любого высокоуровневого языка программирования. И это важно потому что в любых современных языках куда не плюнь постоянно есть и добавляют разнообразные фичи (классы/трейты/прототипы/замыкания и т.д, не говоря про фичи связанные с многопоточностью) и понимание того как все это строится, и какова стоимость абстракций поможет новичку правильно ориентироваться в мире языков и различных подходов и практик программирования (а то например некоторые всерьез сравнивают прототипное программирование с классовым не понимая что они совсем разного уровня — с прототипами доступ к свойству это цикл с перебором свойств по цепочке прототипов когда же в классах это константное считывание по смещению в памяти)

Проблема с бенчмаркингом js заключается в том, что ты никогда не знаешь, что именно оптимизировал компилятор — твой код или (гораздо более вероятно) комбинацию твоего кода и твоего бенчмарка

Всегда можно совершенно точно узнать что наоптимизировал компилятор, достаточно посмотреть ассемблерный код в который компилируется js (через комманду node --print-opt-code --code-comments code.js) и там легко увидеть (по call инструкциям и комментариям) что заинлайнилось а что нет, либо есть инструмент для визуального отображения различных оптимизаций v8 — Turbolizer (оригинальный репозиторий тут а описание и онлайн-версию можно найти тут). А для того чтобы код бенчмарка не влиял на производительность тестируемого кода то можно не мерять производительность а просто прогнать оптимизации для конкретной функции и посмотреть на то что и как инлайнится и для этого просто нужную функцию вызываем через специальную встроенную глобальную функцию %OptimizeFunctionOnNextCall(fn) (которая будет доступна после запуска ноды с флагом node --allow-natives-syntax code.js)


Ну а после того как вы убедились что нужный код заинлайнен в инструкции ассемблера (а иначе он наверняка будет работать медленно так как v8 на текущий момент научился инлайнить очень многое — не только различные циклы, но и методы работы с массивами — всякие .map(), .filter(), .find(), а также создание объектов и функций) то дальше можно совершенно точно (вплоть до тактов) предсказать время работы кода — достаточно понять как работают кеши процессора (https://www.youtube.com/watch?v=JU_RAcsfQVs), немного про то как работает виртуальная память (https://www.youtube.com/watch?v=dFquxC6qTSA) ну и конечно же дока по процессору (https://software.intel.com/en-us/articles/intel-sdm) где с точностью до тактов расписано время работы каждой ассемблерной инструкции

Почему все так любят добавлять кеш к серверу? Это же сразу добавление в проект race-conditions и проблем с консистентностью данных. Советую посмотреть доклад на эту тему — https://www.youtube.com/watch?v=5ZjhNTM8XU8

И какой же по вашему нормальный формат даты и времени? В js unix time можно было получить всегда. А для других форматов получаем слишком много нюансов — https://habr.com/ru/post/146109 и https://habr.com/ru/post/313274/

Information

Rating
Does not participate
Registered
Activity