Рендеринг компонентов блокирует основной потокJavascript

Форум по Javascript
Ответить
Anonymous
 Рендеринг компонентов блокирует основной поток

Сообщение Anonymous »

Я рисую диаграмму с миллионами точек данных, используя облегченные диаграммы + реакцию. Ход выполнения задачи отображается в пользовательском интерфейсе с помощью веб-сокета. Проблема возникает, когда оба запускаются одновременно: происходит заметная задержка перед отображением прогресса в пользовательском интерфейсе, поскольку отрисовка диаграммы, вероятно, блокирует основной поток.
Chart.jsx:

Код: Выделить всё

import {
ColorType,
createChart,
CrosshairMode,
LineSeries,
} from "lightweight-charts";
import { useEffect, useRef } from "react";

export default function Chart({ renders }) {
const chartContainerRef = useRef();

useEffect(() => {
const chart = createChart(chartContainerRef.current, {
layout: {
background: { type: ColorType.Solid, color: "#000000" },
textColor: "#ffffff",
},
width: 600,
height: 300,
grid: {
vertLines: {
visible: false,
},
horzLines: {
visible: false,
},
},
crosshair: {
mode: CrosshairMode.Magnet,
},
timeScale: {
backgroundColor: "#3b3b3b",
timeVisible: true,
rightBarStaysOnScroll: true,
},
});
chart.timeScale().fitContent();
const series = chart.addSeries(LineSeries);
const data = [];
for (let i = 0; i < 1000000; ++i) {
data.push({
time: 1766877868 + i,
value: Math.floor(Math.random() * 2000),
});
}
series.setData(data);
const observer = new ResizeObserver((entries) => {
const { width, height } = entries[0].contentRect;
chart.applyOptions({ width, height });
chart.timeScale().fitContent();
});
observer.observe(chartContainerRef.current);

return () => {
chart.remove();
observer.disconnect();
};
}, [renders]);
return ;
}
App.jsx

Код: Выделить всё

import "./App.css";
import Chart from "./Chart.jsx";
import { useState } from "react";
import DirectTask from "./DirectTask.jsx";

export default function App() {
const [renders, setRenders] = useState(0);
return (




);
}
DirectTask.jsx:

Код: Выделить всё

import { useEffect, useState } from "react";
import axios from "axios";

export default function DirectTask({ setRenders }) {
const [progress, setProgress] = useState("");

useEffect(() => {
(async () => {
const socket = new WebSocket(`ws://localhost:8000/ws/example/1234`);
socket.onmessage = (event) => {
const message = JSON.parse(event.data);
switch (message.type) {
case "progress":
setProgress(((message.current / message.total) * 100).toFixed(2));
}
};
return () => {
socket.close();
};
})();
}, []);
return (

 {
setRenders((prev) =>  prev + 1);
await axios.get("http://localhost:8000/trigger/direct/");
}}
>
Direct {progress}


);
}
Это задача, выполняемая на стороне сервера:

Код: Выделить всё

import json

from asgiref.sync import async_to_sync

def exec_task(channel_layer, task_id):
total = 10000
for i in range(total):
async_to_sync(channel_layer.group_send)(
f'task_{task_id}',
{
'type': 'update_count',
'data': json.dumps(
{'type': 'progress', 'current': i + 1, 'total': total}
),
},
)
В данном случае я использую каналы Django, поэтому вот Consumer.py

Код: Выделить всё

from channels.generic.websocket import AsyncWebsocketConsumer

class ExampleConsumer(AsyncWebsocketConsumer):
def get_group_name(self):
task_id = self.scope['path'].strip('/').split('/')[-1]
return f'task_{task_id}'

async def connect(self):
await self.channel_layer.group_add(self.get_group_name(), self.channel_name)
await self.accept()

async def disconnect(self, close_code):
await self.channel_layer.group_discard(self.get_group_name(), self.channel_name)

async def update_count(self, event):
await self.send(event['data'])
routing.py

Код: Выделить всё

from django.urls import re_path

from core.consumers import ExampleConsumer

websocket_urlpatterns = [
re_path(r'ws/example/(?P\w+)', ExampleConsumer.as_asgi()),
]
Пользовательский интерфейс показан ниже, в нем есть кнопка, которая запускает указанную выше задачу, а также отрисовку диаграммы. Вот как это выглядит со 100 точками данных:
Изображение

Точки данных 3M (отстает и начинает отсчет внезапно с 28%):
Изображение

Что я пробовал до сих пор и не работает:
  • Асинхронный запуск приводит к множеству проблем: одна из них — невозможность вернуть функцию очистки, поскольку вместо этого возвращается обещание, что приводит к дублированию диаграмм, а вторая — иногда портит диаграмму при повторной визуализации, возможно, из-за той же самой причина.
  • Использование веб-воркера, который в данном случае бесполезен, поскольку манипуляции с dom являются узким местом и не поддерживаются этими воркёрами.
Примечание: зависание происходит из-за вызова setData, а не только из-за цикла генерации данных, поэтому даже если я полностью избавлюсь от цикла и отправлю обработанные данные с сервера, это просто улучшает время заморозки, но не предотвращает его.

Подробнее здесь: https://stackoverflow.com/questions/798 ... ain-thread
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»