javascript

JavaScript. Как сделать невероятно быстрый многопоточный Data Grid на 1 000 000 строк. Часть 2/2: р…

  • пятница, 2 мая 2025 г. в 00:00:06
https://habr.com/ru/articles/906082/

Demo | GitHub

Часть 1 Работа с DOM | Часть 2 Работа с потоками

Рисунок 1. Data Grid на 1 000 000 строк
Рисунок 1. Data Grid на 1 000 000 строк

Особенности Fast Data Grid:

  • Невероятно быстрый

  • Многопоточный

  • Работает на пк и телефонах

  • Всего 523 строчки кода

  • Нет зависимостей

  • Vanilla JavaScript

Попробуйте скролл и поиск по 1 000 000 строк - Fast Data Grid.

В статье расскажу про работу с потоками.

Основная идея: вынести поиск в отдельный поток. Показывать результаты поиска частями по мере готовности, не ждать полного завершения поиска

Долгие вычисления блокируют UI.

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

Поиск по 1 000 000 строк - это долго. Пока идет поиск, браузер не будет реагировать на действия пользователя. Браузер “зависнет”. Чтобы убрать зависание, можно вынести поиск в отдельный поток.

Просто вынести поиск в отдельный поток не достаточно. UI не будет зависать, но результаты поиска будут отображаться также долго. Нужно показывать результаты частями по мере готовности. Fast Data Grid сразу отображает первые 100 строк. Пока вы скроллите, Fast Data Grid ищет следующие строки в отдельном потоке.

Данные между потоками передаются долго. Только некоторые типы данных можно передавать по ссылке. Такие типы называются “Transferable objects”

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

Данные между потоками передаются долго. Поэтому передавать массив строк грида со всеми данными для отображения неэффективно.

Эффективно передавать можно только некоторые типы данных. Такие типы данных называются “Transferable objects”. Transferable objects” при передаче между потоками не клонируются, а передаются по ссылке. Uint32Array это “Transferable object”. Мы можем использовать Uint32Array, чтобы передать индексы строк грида.

Рисунок 2. Основной поток и поток поиска хранят копии массива строк с данными. Поток поиска ищет по своей копии и передает индексы строк для отображения в основной поток с помощью Uint32Array
Рисунок 2. Основной поток и поток поиска хранят копии массива строк с данными. Поток поиска ищет по своей копии и передает индексы строк для отображения в основной поток с помощью Uint32Array

setTimeout ставит задачи в конец очереди. С помощью setTimeout можно разбить долгую операцию на подзадачи и не блокировать поток

Поиск по миллиону строк - это долго. Если пользователь меняет строку поиска, нужно как можно скорее прервать предыдущий поиск и начать новый. Способа прервать операцию нет. Но можно разбить поиск на подзадачи. Между подзадачами проверять изменилась ли строка поиска.

setTimeout ставит задачу в конец очереди. См. листинг 1.

setTimeout(_ => console.log(1), 0);
console.log(2);

result
2
1

Листинг 1. setTimeout ставит задачи в конец очереди. В начале в консоле будет 2, потом 1.

В листинге 2 поиск идет по блокам в 5 000 строк. В конце блока setTimeout позволяет потоку среагировать на изменение newStr.

export const wait = () =>
    new Promise(resolve => setTimeout(resolve, 0));


await arrForEachChunk(
    data,
    // chunkSize
    5_000,
    // forEachCallBack
    (row, index) => { /* search */ },
    // chunkEndCallBack
    async () => {
        await wait(); // let other tasks go

        if (_str !== newStr) {
            return false; // stop search
        }

        return true; // continue search
    }
);

Листинг 2. Поиск идет по блокам в 5 000 строк. В конце блока setTimeout позволяет потоку среагировать на изменение newStr

arrForEachChunk - это моя утилита, она есть в репозитории.

Самореклама

Делаю самый красивый редактор блок-схем DGRM.net.
Проверьте сами.

Ставьте звездочки на GitHub.