Как стать автором
Обновить
56
0

Full stack web developer

Отправить сообщение
чтобы был профит от описанной оптимизации, изначальный код должен создавать тысячи объектов, и не классов, а литералов, причем с разным порядком большинства свойств. по-моему это редкость

Это вполне возможно. Самый часто распространенный сценарий: любая функция, принимающая в качестве аргумента объект:


helper({param1: 1, param2: 2})

Это ведь тоже объект, который нужно создать перед передачей в функцию. И такие однотипные объекты-аргументы создаются при каждом вызове функции helper.


влияет ли эта оптимизация на скорость доступа к свойствам объекта после его создания?

Влияет. См. параграф "Встроенные кэши"

Судя по тому, что в мире JS никто не обсуждает влияние и механизмы GC

Это не так. Есть много материалов на эту тему. За примером далеко ходить не нужно: управление памятью, четыре вида утечек памяти и борьба с ними. Тут скорее дело в другом. Начиная работать с JS ты можешь не думать или не понимать как работает GC. Но с ростом начнешь этим интересоваться. И хотя прямого управления сборкой мусора у тебя нет, я считаю очень важным понимать как твой код будет на неё влиять.

Да, это "экономия на спичках", но в случае JavaScript этих "спичек" очень много :)

в JS я не знаю способа остановить распостранение асинков вверх

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


Вместо обычной async функции (которая всегда возвращает промис) пишем функцию которая возвращает либо данные либо промис:


function getValue(key) {
  if (globalCache.has(key)) 
    return globalCache.get(key)

  return new Promise(r => fetchValue().then(value => r(value)))
}

И тогда нужна какая-то функция-обертка, которая будет принимать колбэк и вызывать его на данных, либо после завершения промиса:


function doWithValue(key, callback) {
  const value = getValue(key)

  if (value instanceof Promise)
    value.then(callback)
  else
    callback(value)
}

Так, код будет выполняться синхронно, когда в стеке по факту нет асинхронных вызовов. Но вот удобство такого подхода оставляет желать лучшего.

Собственно об этом и статья — о накладных расходах при использовании асинхронных функций.


Все примеры автора сводятся к простому: если у нас есть какой-то синхронный код, то обернув его в async функцию мы получаем вот столько-то оверхеда.


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


Известно, что await можно использовать только внутри функций или блоков async, а это значит, что если у вас самый нижний уровень API асинхронный, то придется использовать async/await практически везде, даже там, где оно очевидно не нужно.

Действительно такое встречается часто. Когда есть какой-то низкоуровневое хранилище. Ну, для примера вот такая функция


async function getData() {
    if (!globalCache.data) {
        globalCache.data = await readDataFromFS(/* ... */)
    }

    return globalCache.data
}

Фактически такая функция выполняет асинхронную операцию только один раз. А всё остальное время — это самый простое синхронное обращение к свойству объекта. Но не смотря на это, весь стек опирающийся на эту функцию — обязан быть асинхронным. Хоть это и не нужно.


async function getValue(key) {
    return (await getData())[key]
}

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


И вот я вижу такой себе "вирус": Одна асинхронная функция в стеке, делает весь стек асинхронным. Что увеличивает бесполезные накладные расходы.


А к циклам вы цепляетесь напрасно, ибо как я понимаю, они там просто чтобы имитировать множественные запросы. Для более наглядных цифр (ну, чтобы не приходилось сравнивать тысячные и десятитысячные доли секунд)

Так ведь об этом и статья. Мы берём какой-то код. Который по факту является синхронным. И отмечаем его как асинхронный. В JS таких ситуаций ужасно много. Вполне логично, что появляются накладные расходы, но тут именно что указывается их размеры.


Да, "async" код не станет "многопоточным". Да, есть куда более оптимальные варианты обработки множественных асинхронных функций, но статья не об этом, а именно что о размерах накладных расходов.


Я провел собственный небольшой тест и получил просто фантастический оверхед (в несколько тысяч раз). Но в моём случае скорее всего дело в самом тесте и как заметили в этом комментарии мог вмешаться JIT.

Этот код сравнивался с другим:


for (let i = 0; i < 1_000_000_000; i++) {
        j += f(i)
}

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

Но эксперимент показал, что этого делать уже не нужно — console.log в фидле вызывается один раз, даже не смотря на то, что обращение к свойству происходит в цикле

Вы правы, Vue кэширует значения вычисляемых свойств. И при повторном обращении не вычисляет их заново.


НО! Под капотом, он всё равно выполняет некоторую логику, при каждом обращении.


Проверить это очень легко: достаточно замерить время выполнения в обоих случаях:


https://jsfiddle.net/kozack/3a78q4vj/13/


Если посмотреть в консоли, то вы увидите, что staticData вычисляется только один раз, не смотря на многочисленные обращения. И увидите, что optimized вычисляется приблизительно в 10 раз быстрее, благодаря тому, что обращения к staticData сведены к минимуму.

Выглядит так словно json_encode очень плохо спроектирован. Почему так?

А по ссылке написано всё наоборот. Вы тогда внесите ясность, типа «сейчас надо делать так, но в следующей версии всё поменяется и будет по-другому».

В данный момент функциональный компонент это объект с полем рендер функции


export default {
  functional: true,
  render(createElement, context) {
    return createElement('button', 'Click me');
  }
};

В следующей версии он будет упрощен до обычной рендер-функции


const FunctionalComp = props => {
  const theme = inject(themeSymbol)
  return h('div', `Using theme ${theme}`)
}

Не совсем уверен, что вы имели в виду говоря "всё наоборот".


При инициализации системы реактивности и при вызове сеттера он что-то такое делает, да. А при простом обращении почему?

Как я и написал — чтобы построить дерево зависимостей. Другими словами он вызывает гетеры у реактивных свойств, и запоминает что при вычислении result было вызвано свойство base, а значит при изменении base нужно повторно вычислить result.


И почему обработка заблокирует интерфейс?

Потому что так работает JavaScript. В JavaScript нет многопоточности. Если упростить, то вся ваша вкладка работает в одном потоке. Это значит что никакие две задачи не могут исполняться параллельно. И если вы запустите какую-то долгую задачу на JS, то всё остальное будет заблокировано. Попробуйте на любой странице запустить долгий цикл, например:


for (let i = 0; i < Number.MAX_SAFE_INTEGER; i++) {}

И пока он выполняется попробуйте взаимодействовать со страницей: понажимать кнопки, ссылки, какой-нибудь слайдер попереключать.


Также непонятно, на чём основано решение, JobQueue и requestAnimationFrame — это какие-то стандартные вещи или нет, что они делают?

window.requestAnimationFrame() — MDN
splitArray — абстрактная функция которая разбивает массив на части указанного размера и возвращает массив масивов.
JobQueue — абстрактный класс, который принимает какие-то колбеки (задания), и выполняет их один за одним.
Различных реализаций в интернете масса.


Что вообще за «configurable: false» и как оно работает? Гугл выдаёт feature request, который непонятно чем закончился

Object.defineProperty() — MDN


Или как указали в коментариях выше, можно использовать Object.freeze()

НЛО следует удалить этот случайный комментарий

Добавлю несколько советов от себя касательно доступности.


  1. Заблокировать скролл, это наименьшая проблема. Куда важнее не выпускать фокус за пределы всплывающего окна.


  2. При открытии модального окна, нужно обязательно переместить фокус на него. Чтобы скринридеры или ещё что — озвучили, что фокус сместился и куда. Да и при использовании какого-то телевизора, вам не придётся проходить табами всю страницу в поисках как добраться в модальное окно.


  3. Обязательно использовать тег


    <dialog>

    Или секцию с соответствующей ролью


    <section role="dialog">

    Это нужно для того, чтобы ваш блок был семантически опознан как "Диалог".


  4. Внутри модалки, обязательно нужен заголовок. Пусть даже скрытый, чтобы те же скринридеры и ТП озвучили что это за модалка


    <section role="dialog">
    <h2 class="show-screen-reader-only">Подтвердите действие</h2>
    </section>

  5. Если логика модалки предполагает возможность закрыть его без подтверждения (по типу клика на крестик, или клик по кнопке "Отменить") то эту же функциональность необходимо назначить на клик по клавише esc. Так как во многих системах такое поведение является обычным и привычным пользователю. И его необходимо реализовывать в ваших кастомных модальных окнах.


  6. Если модалное окно подразумевает отправку каких-то данных, то внутри обязательно должен быть тег <form>, а саму модалку стоит автоматическии закрывать после успешной отправки данных.


  7. После закрытия модалки, если вы никуда не перенаправляете пользователя, фокус необходимо вернуть туда, где он был на момент открытия модального окна. Сами понимаете, это выбесит любого, если вы перемещаетесь по странице табами, были где-то в середине, открылось модальное окно, а после него вас отправило в начало страницы. И вам снова табать по всему документу в поисках места где вы были.


  8. Бонус: Все скрипты и стили, необходимые для модального окна выносите в отдельный файл. И загружайте непосредственно перед тем как отобразить модалку. Никогда не включайте её в общий бандл.


Вот простой пример:



Вы можете увидеть какие хуки жизненного цикла вызываются при переходе между маршрутами. Каждый компонент создаётся перед переходом, и разрушается после перехода.


Сравните это со вторым примером:



Здесь вы увидите, что каждый компонент создаётся перед первым переходом, но не разрушается. И не создаётся заново при повторном переходе. Компонент сохраняет своё состояние когда вы уходите на другой маршрут.

Нет. Вычисляемое свойство имеет смысл кэшировать только тогда когда оно вызывается в цикле. В вашем случае this.arr будет вызываться только один раз.

Примеры это сборная солянка из нескольких докладов. Пожалуй следует найти и добавить ссылки.

Что вы имеете в виду? С точки зрения CSS это абсолютно стандартный html тег.

Очень интересно посмотреть на какую-то реализацию такого тестирования на базе GitHub actions. По типу как это делают с анализом изменений размера бандла.

Тут больше про доступность. В предложенном варианте кнопка имеет синтаксический текст который будет обработан разными читалками.

Вы можете делать так как я в своих примерах: брать от бутстрапа только стили, а JS добавлять собственный и только там где нужно.

Как я и написал — просто добавить атрибут open


<!-- Содержимое по-умолчанию видимо -->
<details open> ... </details>

При переключении этот атрибут добавляется/удаляется.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность