Eu gostaria de apresentar um pouco de um problema teórico.
Suponha que eu tenha um rolo infinito, implementado algo como o descrito aqui: https://medium.com/frontend-journeys/how-virtual-infinite-scrolling-works-239f7ee5aa58 . Não há nada sofisticado, basta dizer que é uma tabela de dados, por exemplo, NxN, e o usuário pode rolar para baixo e para a direita, como uma planilha, e ele só mostrará os dados na exibição atual mais menos um lidar com.
Agora, digamos também que são necessários aproximadamente 10 ms para "buscar e exibir" os dados nessa exibição, com uma função como:
get_data(start_col, end_col, start_row, end_row);
Isso carrega instantaneamente quando você clica em algum lugar da barra de rolagem ou faz uma 'rolagem leve' para renderizar os dados necessários. No entanto, vamos supor também que, para cada 'evento de busca inacabada', seja necessário o dobro do tempo para renderizar os dados de exibição necessários (devido à memória, gc e algumas outras coisas). Portanto, se eu rolar da esquerda para a direita de maneira lenta e deliberada, posso gerar mais de 100 eventos de rolagem que acionariam o carregamento de dados - no início, há um atraso visivelmente nulo. A busca ocorre em menos de 10ms, mas logo começa a demorar 20ms e depois 40ms, e agora temos um atraso perceptível, até atingir mais de um segundo para carregar os dados necessários. Além disso, não podemos usar algo como uma rejeição / atraso,
Que considerações eu precisaria levar em consideração e como seria um algoritmo de amostra para fazer isso? Aqui está um exemplo da interação do usuário que eu gostaria de ter nos dados, assumindo uma planilha de 10000 x 10000 (embora o Excel possa carregar todos os dados de uma vez) - https://gyazo.com/0772f941f43f9d14f884b7afeac9f414 .
fonte
Respostas:
Acho que você não deve enviar uma solicitação em nenhum evento de rolagem. somente se, com essa rolagem, o usuário chegar ao final da rolagem.
Você também pode especificar uma largura que será definida como próxima o suficiente para buscar novos dados (ei
e.target.scrollHeight - e.target.offsetHeight <= 150
)fonte
Às vezes, a melhor abordagem é um protótipo e, achando o problema interessante, passei um pouco de tempo preparando um, embora, como protótipo, ele tenha muitas verrugas ...
Em suma, a solução mais fácil para limitar um acúmulo de buscas de dados parece simplesmente configurar o mutex de um homem pobre dentro da rotina que está realizando a busca. (No exemplo de código abaixo, a função de busca simulada é
simulateFetchOfData
.) O mutex envolve a configuração de uma variável fora do escopo da função, de modo quefalse
, se a busca estiver aberta para uso e setrue
a busca estiver em andamento no momento.Ou seja, quando o usuário ajusta o controle deslizante horizontal ou vertical para iniciar uma busca de dados, a função que busca os dados primeiro verifica se a variável global
mutex
é verdadeira (ou seja, uma busca já está em andamento) e, nesse caso, simplesmente sai . Semutex
não for verdadeiro, ele será definidomutex
como true e continuará a busca. E, é claro, no final da função de busca,mutex
é definido como false, de modo que o próximo evento de entrada do usuário passe pela verificação mutex na frente e execute outra busca ...Algumas notas sobre o protótipo.
simulateFetchOfData
função, há sleep (100) configurado como uma promessa que simula o atraso na recuperação dos dados. Isso é imprensado com alguns registros no console. Se você remover a verificação mutex, verá com o console aberto que, ao mover os controles deslizantes, muitas instâncias desimulateFetchOfData
são iniciadas e colocadas em suspense aguardando o sono (ou seja, a busca simulada de dados) a ser resolvida, enquanto na verificação mutex no lugar, apenas uma instância é iniciada por vez.mutex
para false, é realizada uma verificação para determinar se os valores de rolagem horizontal e vertical estão alinhados. Caso contrário, outra busca é iniciada. Isso garante que, apesar de vários eventos de rolagem possivelmente não serem disparados devido à busca estar ocupada, que no mínimo os valores finais de rolagem sejam endereçados acionando uma busca final.buffer
é usada para armazenar os dados "buscados". Examiná-lo no console revelará muitas entradas "vazias x XXXX". AsimulateFetchOfData
função é configurada de modo que, se os dados já estiverem retidosbuffer
, nenhuma "busca" seja realizada.(Para visualizar o protótipo, basta copiar e colar o código inteiro em um novo arquivo de texto, renomeie para ".html" e abra em um navegador. EDIT: Foi testado no Chrome e Edge.)
Novamente, este é um protótipo para provar um meio de limitar um acúmulo de chamadas de dados desnecessárias. Se isso tiver que ser refatorado para fins de produção, muitas áreas precisarão ser abordadas, incluindo: 1) redução do uso do espaço variável global; 2) adicionando rótulos de linha e coluna; 3) adicionar botões aos controles deslizantes para rolar linhas ou colunas individuais; 4) possivelmente armazenar dados relacionados em buffer, se cálculos de dados forem necessários; 5) etc ...
fonte
Há algumas coisas que poderiam ser feitas. Eu o vejo como um intercalar de dois níveis, colocado entre o procedimento de solicitação de dados e o evento de rolagem do usuário.
1. Atraso no processamento do evento de rolagem
Você está certo, a renúncia não é nossa amiga nas questões relacionadas à rolagem. Mas existe o caminho certo para reduzir o número de disparos.
Use a versão otimizada do manipulador de eventos de rolagem, que será invocada no máximo uma vez a cada intervalo fixo. Você pode usar o acelerador lodash ou implementar a própria versão [ 1 ], [ 2 ], [ 3 ]. Defina 40 - 100 ms como um valor de intervalo. Você também precisará definir a
trailing
opção para que o último evento de rolagem seja processado, independentemente do intervalo do timer.2. Fluxo de dados inteligente
Quando o manipulador de eventos de rolagem é chamado, o processo de solicitação de dados deve ser iniciado. Como você mencionou, fazer isso sempre que ocorrer um evento de rolagem (mesmo que tenhamos terminado a aceleração) pode causar atrasos no tempo. Pode haver algumas estratégias comuns: 1) não solicite os dados se houver outra solicitação pendente; 2) solicitar os dados não mais de uma vez a cada intervalo; 3) cancelar a solicitação pendente anterior.
A primeira e a segunda abordagens não passam de debouncing e throttling no nível do fluxo de dados. O debounce pode ser implementado com esforços mínimos com apenas uma condição antes de iniciar a solicitação + uma solicitação adicional no final. Mas acredito que o acelerador é mais apropriado do ponto de vista do UX. Aqui você precisará fornecer alguma lógica e não se esqueça da
trailing
opção, pois ela deve estar no jogo.A última abordagem (o cancelamento da solicitação) também é amigável ao UX, mas menos cuidadosa do que a otimização. Você inicia a solicitação de qualquer maneira, mas descarta o resultado se outra solicitação tiver sido iniciada após esta. Você também pode tentar abortar a solicitação se estiver usando
fetch
.Na minha opinião, a melhor opção seria combinar estratégias (2) e (3), para que você solicite os dados apenas se algum intervalo de tempo fixo tiver passado desde o início da solicitação anterior E você cancele a solicitação se outro tiver sido iniciado após .
fonte
Não existe um algoritmo específico que responda a essa pergunta, mas para não haver atraso, é necessário garantir duas coisas:
1. Sem vazamento de memória
Tenha certeza absoluta de que nada no seu aplicativo esteja criando novas instâncias de objetos, classes, matrizes etc. A memória deve ser a mesma depois de rolar por 10 segundos, assim como por 60 segundos, etc. Você pode pré-alocar estruturas de dados se você precisa (incluindo matrizes) e depois as reutiliza:
2. Reutilização constante de estruturas de dados
Isso é comum em páginas de rolagem infinitas. Em uma galeria de imagens de rolagem infinita que mostra no máximo 30 imagens na tela ao mesmo tempo, pode haver apenas de 30 a 40
<img>
elementos criados. Eles são usados e reutilizados à medida que os usuários rolam, para que nenhum novo elemento HTML precise ser criado (ou destruído e, portanto, coletado de lixo). Em vez disso, essas imagens recebem novos URLs de origem e novas posições, e o usuário pode continuar rolando, mas (sem o conhecimento delas) sempre vê os mesmos elementos DOM repetidamente.Se você estiver usando canvas, não usará elementos DOM para exibir esses dados, mas a teoria é a mesma, apenas as estruturas de dados são suas.
fonte