Задать вопрос
  • Как работает сборщик мусор с колбеками Promise?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Сборщики мусора (далее GC) бывают разные, в том же v8 используется сразу 3 типа GC в зависимости от поколения объектов (упрощенно молодое, старое и сложные случаи), но в большинстве своем принцип работы сводится к просчету достижимости из некоторого корня в дереве объектов (например глобального объекта, но не только его). v8 не является исключением, и его GC содержит C++ api для создания таких корней. Из JS мы данным api можем воспользоваться лишь косвенно, через сущности языка предоставляемые либо самим v8 либо платформой (браузером, node.js, deno и т.д.)
    Чтоб было понятно давайте рассмотрим простой пример:
    const h = 'Hello world!';
    const n = 'nothing';
    setTimeout(() => {
      console.log(h);
    }, 1000);
    У нас есть строковые переменные h и n. Переменная n нигде больше не используется и ее память очистится при ближайшей работе GC. А вот переменная h оказалась в замыкании созданном стрелочной функцией. И хотя в JS мы не можем достучаться до h имея ссылку на эту функцию, сама функция все таки имеет ссылку на h, а значит h не может быть уничтожена пока не будет уничтожена сама функция. В терминах GC ссылка на h будет серой, то есть сама ссылка на h недоступна из корня напрямую, но сейчас мы проверяем объекты, которые на нее ссылаются и истина будет зависеть от них (подробнее можете погуглить "mark black white and gray memory in gc").
    Давайте посмотрим на саму стрелочную функцию, которая держит h в замыкании. Из кода видно, что мы ее передаем в функцию setTimeout, о которой известно, что это api предоставленное платформой (а значит вероятно какая-то часть написана на C++), а так же, что она асинхронная. Платформе реализующей setTimeout наша функция понадобится после асинхронного ожидания и никто платформе не сможет гарантировать, что во время этого ожидания не будет работы GC, поэтому ей ничего не остается, кроме как запросить у v8 создание нового корневого дерева объектов, в которое и будет положена ссылка на данную функцию.
    После же выполнения таймаута платформе больше не нужна наша функция, поэтому ссылка на нее будет удалена из дерева объектов. А так как других ссылок на функцию нет, и она больше не доступна ни из одного корня - GC удалит из памяти и функцию и строку связанную h, которая так же стала недоступна из корня.

    Посмотрим на пример из вопроса. У нас есть стрелочная функция, которая удерживает на себе инстанс компонента через this ссылку (так как стрелочные функции замыкают this). Саму функцию в памяти удерживает промис порожденный вызовом loader('url'), так как мы отдали её в метод then. Других ссылок на данную функцию нет, а значит и сама функция и ее замыкание (инстанс компонента) будут "жить" не менее "жизни" промиса.
    Скажем был отправлен запрос на сервер, но потом компонент в котором был объявлен промис и колбек был удален.
    И после удаления приходит ответ от сервера, и он выполнит колбек. Это значит что колбек остался в памяти со всеми переменными контекста
    Если других ссылок не осталось, то инстанс компонента будет удерживаться от очистки через промис.

    Теперь стоит разобраться с самим промисом. У него может быть 3 состояния - pending, resolved или rejected. После перехода в состояния resolved или rejected промис может выполнить сохраненные колбэки в ближайшем микротаске, а после он удалит на них ссылки из себя, в следствии чего, память удерживаемая замыканием колбэка может быть очищена (при отсутствии на нее других ссылок, достижимых из какого-либо корня).
    В состоянии pending промис может потенциально находится бесконечно долго, при этом ссылаясь на все колбэки переданные ему в методы then, catch или finally, а значит так же косвенно ссылаясь на их замыкания. И тут все зависит от того, кто ссылается на данный промис, и достижим ли он из корня. И да, промис вполне может быть удален из памяти так и не дождавшись своего завершения.
    интересное умозаключение
    Если Promise - это обещание, то в данном случае оно будет нарушено?


    В комментах к вопросу есть еще один интересный пример:
    function getSomething(){
      return new Promise((resolve, reject)=>{
        if(sys_condition){
           resolve();
        } 
      })
    }
    
    function testPromise(){
      let config = {....}
      getSomething().then(()=>{
         #use config
         goOn(...config)
      })
    }
    
    testPromise();
    У нас есть вызванная 1 раз функция testPromise, которая получает из функции getSomething промис, в который с помощью метода then сохраняет колбэк, удерживающий в замыкании переменную config. Сам промис она нигде не сохраняет, что здесь очень важно.
    В функции getSomething мы просто возвращаем промис созданный через его конструктор, который как мы уже знаем нигде больше не сохраняется. И на этом могло бы все и закончится, без вызова колбэка независимо ни от чего. Но конструктор промиса выполняет свой колбэк синхронно, а кроме того он передает в него 2 функции - resolve и reject, которые в своем замыкании ссылаются на только что созданный промис (а это уже 2 ссылки на него, хотя мы то его никуда не сохраняли). Переменная reject никак не используется, а значит спокойно может быть удалена после завершения колбэка. Переменная resolve просто вызывается как функция внутри условия, но более тоже никак не используется и никуда не сохраняется, а значит так же может быть удалена.
    В этом примере. если sys_condition = false и resolve не вызовется, это значит что создается утечка памяти
    Нет, утечки памяти не будет. Колбэк созданный в testPromise будет удален вместе с замыканием, так и не вызвавшись ни разу.
    Ответ написан
    3 комментария
  • Как отсортировать объект по вложенному массиву?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    const newArray = arr.slice(); // так как sort мутирует массив, а нужен новый
    const isAnnaSkill = skill => skill.name === 'Anna';
    const getAnnaLevel = item => item.skills.find(isAnnaSkill)?.level;
    newArray.sort((a, b) => getAnnaLevel(b) - getAnnaLevel(a));
    Ответ написан
    2 комментария
  • Как обработать 2 запроса одновременно в node.js?

    bingo347
    @bingo347 Куратор тега Node.js
    Crazy on performance...
    Комментировать
  • Как прокачать навыки функционального программирования?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Я бы начал с изучения книги Луиса Атенсио "Функциональное программирование на ...
    Дальше можно изучить серию статей "Мышление в стиле Ramda" на хабре
    Ответ написан
    Комментировать
  • Как узнать какой global user.name и global user.email и как переключиться на старом акаунте гитхаба?

    bingo347
    @bingo347
    Crazy on performance...
    git config --global --get user.name
    git config --global --get user.email

    С аккаунтом github это никак не связано, все хранится локально у Вас на компе

    Глобальные конфиги лежат в файле $HOME/.gitconfig
    Локальные - в ближайшей папке .git вверх по дереву
    Ответ написан
    Комментировать
  • Что следует почитать/посмотреть для изучения Git?

    bingo347
    @bingo347
    Crazy on performance...
    Ответ написан
    Комментировать
  • Как посчитать количество повторений символов в строке?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Воспользуйтесь для подсчета символов обычным объектом, где ключи - буквы, а значения - их количество, а уже потом сформируйте из него массив.
    Ответ написан
    Комментировать
  • Как отфильтровать массив?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    То что у Вас в полях created и viewed вполне можно отдать в конструктор Date, а даты уже можно сравнивать между собой
    console.log(new Date("2020-05-22 16:24:16")); // работает

    Дальше, последний created, тут нужно уточнение:
    Если массив messages всегда упорядочен по полю created, как и в примере, то можно просто брать последний элемент массива и работать с ним:
    const lastMessage = messages[messages.length - 1];
    console.log(lastMessage);

    Если такой гарантии нет, то нужно искать максимум:
    const lastMessage = messages.reduce(([maxDate, maxMessage], currentMassage) => {
      const currentDate = new Date(currentMassage.created);
      if(currentDate > maxDate) {
        return [currentDate, currentMassage];
      }
      return [maxDate, maxMessage];
    }, [-Infinity, null])[1];
    console.log(lastMessage);

    Ну а если с этим разберетесь, то с последним пунктом у Вас уже не должно быть проблем, тут все аналогично
    Ответ написан
    3 комментария
  • Map это объект или массив?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    В js все объект, что не является примитивом (простые объекты, экземпляры классов, массивы, Map, Set, функции).
    Так что да, Map это объект.
    А вот массивом он не является, ибоArray.isArray(new Map()) // false
    Здесь есть вот такая штука [[Entries]]. А entries возвращает массив свойств объекта
    Вообще-то entries возвращает итератор. Итератор - это тоже не массив.
    Ответ написан
    2 комментария
  • Полноэкранный режим на сайте?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Проблема в том, что при переходе по ссылке документ, на котором вызван фулскрин, перестает существовать, а вместо него формируется новый, и при этом происходит выход из фулскрина.
    Решения тут я вижу два:
    1. Костыльное. Кроме документа фулскринить еще можно video и iframe, и iframe может тут помочь, так как он останется неизменным, сколько бы страниц в него не загружалось.
    2. Сложное. Можно переделать сайт в SPA, тогда настоящих переходов не будет, и все страницы будут работать в пространстве одного документа
    Ответ написан
  • Есть ли эффект-анимация перелистывания страниц книги со сменой url?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    А заглянуть как у них сделано?
    Вся магия тут: megaweb.su/demo/css3_book/css/style.css
    Ответ написан
  • Как разобрать HTTP запрос?

    bingo347
    @bingo347
    Crazy on performance...
    Что делать если размер запроса больше чем размер буфера?
    Читать в цикле. read вообще не гарантирует, что заполнит буфер полностью, но больше его размера он точно за раз не прочитает. А еще он возвращает io::Result<usize>, в котором сообщает, сколько реально байт было прочитано.
    1.1) Нужно будет читать пока не найдется CRLF в буфере ?
    Пока read не вернет Ok(0), ну или ошибку. Хотя с ошибкой не все так однозначно, согласно доке может вылететь Err(io::ErrorKind::Interrupted) при котором стоит повторить попытку чтения. Вообще CRLF будет после каждого заголовка, а когда заголовки закончатся будет 2 CRLF подряд, а потом еще может быть тело запроса, а может и не быть.
    1.2) для этого мне нужно пройтись по буферу и искать CRLF . если его нет то очистить буфер и продолжать читать ?
    нет, нужно распарсить то что пришло, куда-то сохранить, а потом продолжить чтение.
    2) Как отделять один http запрос от другого ?
    Если у нас не keep-alive, то каждый запрос будет в отдельном соединении, но keep-alive наступает только если обе стороны прислали заголовок Connection: keep-alive Можете сделать по простому, и отвечать с заголовком Connection: close, все равно в учебном проекте производительность у Вас будет никакая. Но если хотите все же заморочиться, то правило тоже не сложное - следующий запрос начинается в следующем же байте, где закончился текущий. Размер тела запроса в байтах можно узнать из заголовка Content-Length, а если его нет, то можете считать, что его значение 0.

    Что следует почитать для Вашей задумки:
    1. Спеку http
    2. Исходники hyper
    3. Исходники actix-web
    Ответ написан
    Комментировать
  • TS React children state?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Или нормально типизировать эту функцию (зачем Вам вообще ts, если Вы пишите any?) ну или заткнуть в тип стейта поле вида[k: any]: any
    Ответ написан
  • Не копируется массив, в чем причина?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Не копируется, потому-что внутри массива maze у Вас лежат ссылки на подмассивы, и Вы копируете лишь их, а не сами массивы.
    Придется скопировать на уровень глубже:const mazeCopy = maze.map(sub => sub.slice())
    Ответ написан
    Комментировать
  • Как правильно разбивать зависимости npm пакета?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Хорошей практикой является не тащить зависимости в бандл.
    В webpack есть externals
    В rollup есть external
    Если Вы вшиваете свои зависимости в бандл своего пакета - Вы без вариантов создаете дублирование кода в бандле, если мне нужны те же зависимости, что и Вашему пакету и дополнительно Ваш пакет
    Ответ написан
  • Как установить gulp через npm?

    bingo347
    @bingo347
    Crazy on performance...
    Зачем Вы установили https-proxy опцию в конфиг npm да еще и криво? Все Ваши проблемы от копипасты без понимания, что Вы копипастите
    Ответ написан
  • Изменение action в форме. Корректно или нет?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    JS отработает раньше запросов, можете не париться
    Лучше задумайтесь, а что будет, если у меня не работает Ваш JS?
    Ну еще советую почитать про fetch апи, а так же, что по мимо click есть другие события
    Ответ написан
  • Нужно ли писать тесты(Jest/Enzyme) в React приложении которое написано на TypeScript?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Самый вредный миф, который есть в typescript сообществе: "Типизация защитит от ошибок"
    Не защитит!
    Типизация не для этого, она для ускорения и удешевления долгосрочной разработки.
    Ну а если еще вспомнить, что у typescript структурная типизация (никто не мешает складывать рубли с килограммами) и при этом в ней есть тип any - то сразу становится понятно, что ни о какой надежности говорить не приходится.
    Ответ написан
    Комментировать
  • Как я могу прочитать файл на другой локальной машине?

    bingo347
    @bingo347 Куратор тега Node.js
    Crazy on performance...
    что-то мне кажется что надо слеши экранировать пробовал так /\ ничего не выходит
    Да, обратный слэш нужно экранировать, так как он сам является экранирующим символом, прямой же слэш не экранирует, поэтому ничего не выходит
    Пример:
    \\ -> \
    \\\\ -> \\

    ну и модуль path в ноде, достаточно умный, чтоб привести слэши в путях к используемым в системе, поэтому можно просто
    path.normalize( '//192.168.1.5/C$/data/data.json' )
    Ответ написан
    5 комментариев