Задать вопрос
  • Как правильно обрабатывать многомерные массивы в typeScript?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Проблема в ...form.formParams.errorList[formIndex]
    Давайте по порядку:
    // допустим у нас есть следующие переменные с типами:
    let obj: Obj;
    let value: T;
    
    // мы их собираем в такой объект:
    const newObj = { ...obj, k: value };
    тип для newObj компилятор будет выводить из контекста и получит Obj & { k: T }, ровно из того, что мы объединяем эти сущности.

    Усложним пример:
    let objs: Obj[];
    let value: T;
    let key: string;
    
    const newObj = { ...objs[1], [key]: value };
    Тип на выходе будет ужеObj & { [key: string]: string }, хотя objs на это раз у нас массив, компилятор видит, что мы берем индекс.

    Что будет, если objs вместо простого Obj[] у нас будет Obj[] | Obj[][]?
    Сначала мы берем значение по индексу из objs - эта операция вернет тип Obj | Obj[].
    После мы разворачиваем его через spread в объект и дописываем значение по индексу, получаем тип:(Obj | Obj[]) & { [key: string]: string }, тут еще Важно понимать, что типы в TS - это чистая математика, и данный тип полностью равносилен такому:
    (Obj & { [key: string]: string }) | (Obj[] & { [key: string]: string })

    А что, если Obj - это тоже { [key: string]: string }? Мы сможем свернуть тип, упростив уравнение на типах:
    (Obj & Obj) | (Obj[] & Obj)
    (Obj) | (Obj[] & Obj)
    (Obj & Obj[]) | (Obj & Obj)
    (Obj & Obj[]) | (Obj)
    Obj & Obj[]
    И это именно тот тип, который получил у Вас компилятор
    Ответ написан
    Комментировать
  • Как исправить ошибку used of moved value?

    bingo347
    @bingo347
    Crazy on performance...
    temp = temp.into_iter().rev().collect();
    (0..cols).for_each(|col| vec[row][col] = temp[col]);

    temp владеет некоторой памятью
    (0..cols).for_eachитерирует и вызывает переданную ему функцию cols раз
    замыкание преданное в for_each захватывает vec по &mut ссылке, а temp по значению
    на первой итерации temp[0] делает move в vec[row][0], так как в memory model Rust ни один тип не может существовать в виде "огрызка с дыркой", компилятор дописывает drop(temp) в конец функции |col| vec[row][col] = temp[col], а так как temp захвачен замыканием по значению (по сути он является частью замыкания), то в конце итерации цикла внутри for_each еще и само замыкание дропается.
    Другими словами for_each хочет от вас FnMut, а Вы ему дали только FnOnce.

    Думаю Вам поможет одна из этих функций:
    https://doc.rust-lang.org/beta/std/mem/fn.take.html
    https://doc.rust-lang.org/beta/std/mem/fn.replace.html

    index out of bounds: the len is 2 but the index is 2
    Это не ошибка компиляции, а runtime panic, которая говорит о там, что Вы вышли за границу слайса

    P.S. я надеюсь у Вас усть понимание, что здесь O(n3)?
    while (0..rows).map(|r| vec[r].first().unwrap()).all(|&x| x == 0) {
        (0..rows).for_each(|r| {vec[r].remove(0);});
    };
    Ответ написан
    Комментировать
  • Хайп вокруг ЯП Rust и C?

    bingo347
    @bingo347
    Crazy on performance...
    Насколько критичной проблемой для программиста является ручное управление памятью, которое называют недостатком языка Си?
    Неосвобожденная память (утечка памяти) - это самое безобидное, что может произойти.
    - Сделать free дважды - это UB
    - Забыли проверить результат malloc/calloc/realloc на не NULL, разыменовали - UB
    - Почитали память, в которую ни разу не писали - UB
    - Разыменовали указатель после free - UB
    - Гонка данных - UB
    - ...и еще дофига всего, от чего в лучшем случае программа просто будет работать неправильно, например спалит Ваш пароль, или переведет Ваши деньги на другой счет 10 раз.

    Новый язык программирования Раст, как заявляют, лишен этого недостатка

    Система типов Rust гарантирует лишь одно - в safe коде не будет UB. Утечка памяти, кстати, не является UB, поэтому память вполне себе можно утечь в safe коде, помимо циклических счетчиков ссылок std дает немало возможностей сделать это напрямую:
    https://doc.rust-lang.org/beta/std/mem/fn.forget.html
    https://doc.rust-lang.org/beta/std/mem/struct.Manu...
    https://doc.rust-lang.org/beta/std/boxed/struct.Bo...
    https://doc.rust-lang.org/beta/std/vec/struct.Vec....

    но разве число ошибок в программе зависит именно от наличия или отсутствия ручного управления памятью
    В Rust ручное управление памятью, как и в C и в C++, просто есть культура, что если некая структура аллоцировала память, то она ее освободит. Всякие Vec, Box и т.д. делают это в Drop. В C++ многие повседневные типы так же освобождают в деструкторе память, которую они выделили. Однако в Rust есть разделение на safe и unsafe код и для прикладного программирования safe возможностей более чем достаточно. В C++ же весь код unsafe.

    разве общее число ошибок не перераспределяется на другие недостатки программы
    Нет, не перераспределяется. Хорошая система типов действительно может избавить от многих ошибок, что в целом сделает ПО более надежным. Но надо понимать, что от всех ошибок не избавит ни что. Банальная дискоммуникация с заказчиком порождает огромное число багов.
    Но в Rust очень быстро привыкаешь к такому замечательному подходу в разработке, как Type Driven Development, который позволяет предупредить многие ошибки еще до написания кода. После Rust я стал применять этот подход и в других ЯП, и он работает очень хорошо, даже там, где типизация не такая строгая.

    являются ли ошибки с памятью ошибками программиста, а не компилятора и языка программирования
    Безусловно это ошибки программиста. Программисты, как правило, - это люди, а людям свойственно ошибаться. И хорошо, когда есть средства статического анализа, которые помогают предотвращать ошибки до выхода ПО в продакшн.

    P.S. Вот интересная статья про Rust к прочтению: https://steveklabnik.com/writing/a-sad-day-for-rust
    И к чему она привела: https://github.com/fafhrd91/actix-web-postmortem
    Ответ написан
    4 комментария
  • Как описать обобщённый ответ с restful api сервера?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Используйте дженерик параметры
    export type ApiResponse<T> =
      | { errors: Array<{ [key: string]: string }> }
      | { data: Array<T> }
      | { text: string };

    abstract class Api<T> {
      protected readonly baseUrl: string;
      protected readonly endPoint: string;
    
      protected get url(): string {
        return new URL(this.endPoint, this.baseUrl).toString();
      }
    
      protected constructor(baseUrl = '/', endPoint: string) {
        this.baseUrl = baseUrl;
        this.endPoint = endPoint;
      }
    
      protected async http({
        url = '',
        method = 'GET',
        data = undefined,
      }: RequestData): Promise<ApiResponse<T>> {
        try {
          const response: Response = await fetch(
            (new URL(url, this.url).toString()),
            { method, body: data }
          );
          return await response.json();
        } catch (e) {
          console.error(e.message);
          return { text: e.message };
        } finally {
          // finnally
        }
      }
    }

    class ApiPost extends Api<Post> {
      constructor(baseUrl: string, endPoint: string) {
        super(baseUrl, endPoint);
      }
    
      async list(count?: number): Promise<false | Array<Post>> {
        const response: ApiResponse = await this.http({
          url: `${count ? `?_limit=${count}` : ''}`,
        });
    
        if (Array.isArray(response))
          return response;
    
        return false;
      }
    }
    Ответ написан
    4 комментария
  • Как вытянуть ключ с обьекта который находится в массиве?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Ваша проблема в том, что у Вас массив объектов, а Вы пытаетесь с ним работать как с единственным объектом. Прежде чем работать с объектом, нужно получить ссылку на него из массива, например по индексу.

    Еще одна проблема, хоть она и не относится к вопросу, это то что у Вас присутствует бесполезная работа с localStorage (Вы каждый раз пишите туда новые, никак не зависящие от старых, данные, только ради того чтоб их тут же прочитать), а так же бесполезная сериализация/десериализация JSON.
    Отношение к пользовательским ресурсам - все равно что вломится к пользователю домой и плюнуть ему в лицо.
    Ответ написан
    Комментировать
  • Как скопировать файлы в папку Nginx через Node.js?

    bingo347
    @bingo347 Куратор тега Node.js
    Crazy on performance...
    1. Создать новую группу пользователей
    2. Добавить в нее пользователя nginx (обычно www-data) и пользователя из под которого запускается node
    3. Назначить эту группу владельцем папки, в которую нужно писать и открыть права на запись для группы
    Ответ написан
    Комментировать
  • Как правильно отправить на сервер и сохранить буфер загружаемого файла?

    bingo347
    @bingo347
    Crazy on performance...
    Во-первых, строки в JS в памяти хранятся в виде utf-16, а значит сохранение произвольного бафера в строку может сломать данные.

    Вот тут Ваши данные ломаются:
    let chunks = '';
    
    req.on('data', chunk => {
        chunks += chunk;
    });


    Во-вторых, не стоит читать файлы в память целиком. Память не резиновая, и однажды ее может просто не хватить.

    В-третьих, не нужно перекладывать байты из одного потока в другой руками, есть pipe, который делает это гораздо эффективнее.

    Отправка:
    const filePath = `../source/archive.zip`;
    const reader = fs.createReadStream(filePath);
    const options = {
        hostname: url.hostname,
        port: 443,
        path: url.pathname,
        method: 'PUT',
        headers: {
            'Content-Type': 'application/zip',
            'Content-Length': fs.statSync(filePath).size
        }
    };
    const req = https.request(options, res => {
        res.on('error', err => console.log('ошибка:', err));
        res.on('end', () => console.log('отправлено'));
    });
    reader.pipe(req);

    Получение:
    router.put('/', (req, res, next) => {
        const pathToFile = path.join(__dirname, '../public/uploads/archive.zip');
        const writer = fs.createWriteStream(pathToFile);
        req.pipe(writer).once('error', next).once('finish', () => {
            res.send('файл сохранён');
        });
    });
    Ответ написан
    4 комментария
  • Как сохранять события в localStorage js?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    localStorage.getItem сам не поставит класс элементу, он просто читает значение
    if (localStorage.getItem('theme') !== null) {
        localStorage.getItem('theme') // эта строка ничего не делает
    }


    Попробуйте так:
    (function () {
      const blue = document.querySelector('#pink')
    
      switch (localStorage.getItem('theme')) {
          case 'blueTheme':
              blue.classList.add('blueTheme')
      }
    
      blue.addEventListener('click', () => {
        if (blue.classList.contains('blueTheme')) {
            blue.classList.add('blueTheme')
            localStorage.setItem('theme', 'blueTheme')
        } else {
            blue.classList.remove('blueTheme')
            localStorage.removeItem('theme')
        }
      })
    }())
    Ответ написан
    Комментировать
  • Как правильно загружать файлы на сервер Node.js?

    bingo347
    @bingo347 Куратор тега Node.js
    Crazy on performance...
    Чтоб не было проблем путей в разных окружениях, всегда стройте пути с использованием переменной __dirname или других путей, полученных на ее основе.
    Для соединения нескольких частей пути в полный путь используйте path.join

    Большинство библиотек относительные пути строят от process.cwd(), который напрямую зависит от того из какой папки был запущен скрипт.
    Ответ написан
    Комментировать
  • Какой инструмент использовать для синхронной очереди задач?

    bingo347
    @bingo347 Куратор тега Node.js
    Crazy on performance...
    Так понимаю сами задачи асинхронные, но их последовательно надо выполнять.
    Обычного массива функций и промисов хватит за глаза:

    Вариант 1:
    function series(tasks) {
        return tasks.reduce((p, task) =>
            p.then(() =>
                task()), Promise.resolve());
    }
    
    series([
        async () => { /* ... */ },
        async () => { /* ... */ },
        async () => { /* ... */ },
        async () => { /* ... */ },
    ]);


    Вариант 2:
    async function series(tasks) {
        for (const task of tasks) {
            await task();
        }
    }
    
    series([
        async () => { /* ... */ },
        async () => { /* ... */ },
        async () => { /* ... */ },
        async () => { /* ... */ },
    ]);
    Ответ написан
    Комментировать
  • В чем разница двух конструкций в js?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    $(function() {
    
    });
    вызывает функцию $ и отдает ей анонимную функцию в качестве аргумента

    (function($) {
    
    })(jQuery);
    Вызывает анонимную функцию сразу, параметр jQuery попадет в аргумент $ внутри функции
    Ответ написан
    Комментировать
  • Как сделать запрос в результатах запроса MongoDB?

    bingo347
    @bingo347
    Crazy on performance...
    https://docs.mongodb.com/manual/reference/operator...
    https://docs.mongodb.com/manual/reference/operator...
    https://docs.mongodb.com/manual/reference/operator...

    А вообще, монга для такого не предназначена... БД нужно под задачу выбирать, а не следуя моде...
    Ответ написан
    Комментировать
  • Webpack 5: что значат эти ошибки при сборке проекта?

    bingo347
    @bingo347
    Crazy on performance...
    :warning: Since webpack v4 the extract-text-webpack-plugin should not be used for css. Use mini-css-extract-plugin instead.
    https://v4.webpack.js.org/plugins/extract-text-web...

    Вам нужен https://v4.webpack.js.org/plugins/mini-css-extract...
    Ответ написан
  • Form: input и label. Полезно ли всегда использовать label?

    bingo347
    @bingo347 Куратор тега HTML
    Crazy on performance...
    Как вы считаете, всегда ли необходим label в связке с input? Даже если нет текста, по которому можно кликнуть и перескочить в поле ввода. Или допустим, тег label, не используется в качестве контейнера.
    Пустой label крайне вреден, особенно если его можно кликнуть и клик приводит к непредсказуемому поведению - взаимодействию с input, взаимодействию, которого не ожидает пользователь. Все не ожиданное ведет к плохому UX.
    Хотя о каком UX я говорю?
    Если у нас невозможно купить мышку онлайн не пользуясь мышкой (в том числе ее заменителями вроде тачскрина или тачпада)... Я пробовал как-то, обошел первые 2 страницы в яндексе по запросу "купить мышку"... Самое обидное было, что на некоторых сайтах все обламывалось уже на форме заказа...

    Встречал примеры, где тег label, связывают через идентификатор с input и при этом, прячут его, с помощью visually-hidden. Зачем? Причём так делают, некоторые школы и опытные разработчики.
    Так делают для скринридеров, когда придурок дизайнер не предусмотрел label в дизайне, а без него никак. Делаем нормальный UX, не ломая визуал заложенный тем ущербом, который "я дисигнер я так вижу".

    P.S. совет от меня лично, выделяйте фокус не только для input, но и для связанного с ним label, это удобно.
    Ответ написан
    4 комментария
  • В этом случае объект очищается по причине работы алгоритма Mark-and-sweep или это просто эффект работы локальной переменной...?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Все сильно зависит от движка, но в основном принципы похожи. Расскажу на примере v8 (chrome, node).
    Первое, что надо понять, в v8 сборка мусора основана на поколениях. И в разных поколениях применяются разные алгоритмы. В v8 используется 3 поколения:
    - Молодое (собирается по наполнению (часто), но быстрым упрощенным алгоритмом)
    - Старое (собирается по расписанию (редко), здесь как раз Mark & Sweep)
    - Особое (я не очень про него знаю, здесь очень большие объекты + объекты с прямым доступом из глобального)

    Второе, что нужно понять, все данные доступные из JS v8 хранит на куче, независимо примитив это или js-объект. С точки зрения GC все есть объект, и числа и строки и функции.

    Теперь более подробно про молодое поколение. Его цель - быть быстрым, быстро выделять и освобождать память. Создатели v8 прекрасно знают, что аллокация на куче - крайне затратная операция, поэтому здесь преаллоцированы 2 страницы памяти, которые используются по очереди. Заодно можно получить бонус в работе с процессорным кэшем, за счет того, что здесь "горячие" данные и они лежат рядом, а значит попадут на одну кэш линию. Когда мы создаем новый объект, v8 просто помещает его в конец активной страницы. Если места не хватает происходит быстрая сборка мусора. А еще здесь используется подсчет ссылок (он быстрее), но только для ссылок из старого поколения - если есть хоть 1 такая ссылка - объект живой, а так же живо все его объектное дерево. Так же нельзя "убивать" объект, если на него есть ссылки из стэка. Все остальное "мертвое". Живые объекты переносятся в начало второй страницы памяти (заодно происходит дефрагментация памяти), после чего она становится активной. Если объект пережил 3 таких быстрых сборки, то вместо второй страницы его переносят на старое поколение, при этом происходит аллокация памяти.

    В Вашем примере задействовано только молодое поколение.
    function f() {
        // это мертвый код, после оптимизации f строка вообще не будет память использовать
        let a = 'some text';
    
        // это 2 молодых объекта, на них уже ссылается контекст вызова f, а на него ссылается стэк
        var obj1 = {};
        var obj2 = {};
        obj1.p = obj2; // obj1 references obj2
        obj2.p = obj1; // obj2 references obj1. This creates a cycle.
    
        // при завершении функции стэк перестает ссылаться на контекст вызова
        // контекст вызова умрет при ближайшей GC,
        // а вместе с ним и obj1 и obj2, так как их никто не отметит "живыми"
    }
    f();
    Ответ написан
    Комментировать
  • Как получить промисо-завершающий callback от асинхронной функции?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Если правильно понял замысел, то можно как-то так:
    async function test() {
        let savedResolve, sevedReject;
        const promiseForReturn = new Promise((resolve, reject) => {
            // этот колбэк отрабатывает синхронно
            sevedResolve = resolve;
            savedReject = reject;
        });
        // поэтому savedResolve и sevedReject уже доступны здесь
        sevedReject(1);
        return promiseForReturn; // промис возвращаемый test() будет связан с этим и ждать его
    }

    Но лучше с таким все же быть аккуратным
    async function test() {
        let savedResolve, sevedReject;
        const promiseForReturn = new Promise((resolve, reject) => {
            sevedResolve = resolve;
            savedReject = reject;
        });
        // это deadlock
        await promiseForReturn;
        sevedResolve(1);
    }
    Ответ написан
  • Есть кастомный вариант fullpage js?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Комментировать
  • Для чего нужны getters и setters в JavaScript?

    bingo347
    @bingo347 Куратор тега JavaScript
    Crazy on performance...
    Геттеры и сеттеры позволяют навешивать логику на чтение и запись свойств.

    Например фреймворк Vue реализует через них реактивность. В геттере он сохраняет для свойства информацию, кто его читал, можно сказать, что свойство так "понимает", кто от него зависит. А в сеттере свойство уведомляет всех, что оно поменялось, а значит нужно пересчитать и то, что от него зависит.

    Еще один пример использования - ленивые вычисления. Например, мы можем создать middleware для express, который парсит куки и добавляет свойство cookies в объект запроса. Вот только куки нужны не каждый запрос, а их парсинг отнимает время. Через геттер можем сделать, чтоб парсинг происходил только при первом обращении к свойству cookies, тем самым улучшив производительность.
    Ответ написан
    Комментировать