Me gustaría plantear un problema teórico.
Supongamos que tengo un desplazamiento infinito, implementado algo como lo descrito aquí: https://medium.com/frontend-journeys/how-virtual-infinite-scrolling-works-239f7ee5aa58 . No tiene nada de lujos, basta con decir que es una tabla de datos, digamos NxN, y el usuario puede desplazarse hacia abajo y hacia la derecha, como una hoja de cálculo, y solo mostrará los datos en la vista actual más menos un encargarse de.
Ahora, digamos también que se necesitan aproximadamente 10 ms para "buscar y mostrar" los datos en esa vista, con una función como:
get_data(start_col, end_col, start_row, end_row);
Esto se carga instantáneamente al hacer clic en algún lugar de la barra de desplazamiento o al hacer un 'ligero desplazamiento' para representar los datos necesarios. Sin embargo, supongamos también que para cada 'evento de recuperación no terminado', se tarda el doble de tiempo en procesar los datos de vista necesarios (debido a la memoria, gc y algunas otras cosas). Entonces, si me desplazo de izquierda a derecha de una manera lenta y deliberada, podría generar más de 100 eventos de desplazamiento que desencadenarían la carga de datos; al principio, no hay un retraso notable. La recuperación ocurre en menos de 10 ms, pero pronto comienza a tardar 20 ms, y luego 40 ms, y ahora tenemos algo así como un retraso notable, hasta que llegue más de un segundo para cargar los datos necesarios. Además, no podemos usar algo como un rebote / retraso,
¿Qué consideraciones necesitaría tener en cuenta y cómo sería un algoritmo de muestra para lograr esto? Aquí hay un ejemplo de la interacción del usuario que me gustaría tener en los datos, suponiendo una hoja de cálculo de 10000 x 10000 (aunque Excel puede cargar todos los datos a la vez): https://gyazo.com/0772f941f43f9d14f884b7afeac9f414 .
fuente
Respuestas:
Creo que no debe enviar una solicitud en ningún evento de desplazamiento. solo si con este desplazamiento el usuario llega al final del desplazamiento.
También puede especificar un ancho que se definirá como lo suficientemente cerca como para obtener datos nuevos (ei
e.target.scrollHeight - e.target.offsetHeight <= 150
)fuente
A veces, el mejor enfoque es un prototipo, y al encontrar el problema interesante, pasé un poco de tiempo cocinando uno, aunque como prototipo tiene muchas verrugas ...
En resumen, la solución más fácil para limitar la acumulación de datos acumulados parece ser simplemente configurar el mutex de un pobre dentro de la rutina que realiza la recuperación. (En el ejemplo de código a continuación, la función de recuperación simulada es
simulateFetchOfData
). El mutex implica establecer una variable fuera del alcance de la función, de modo que sifalse
la recuperación está abierta para su uso y sitrue
la recuperación está actualmente en curso.Es decir, cuando el usuario ajusta el control deslizante horizontal o vertical para iniciar una búsqueda de datos, la función que busca los datos primero verifica si la variable global
mutex
es verdadera (es decir, una búsqueda ya está en curso), y si es así, simplemente sale . Simutex
no es verdadero, se establecemutex
en verdadero y luego continúa realizando la búsqueda. Y, por supuesto, al final de la función de recuperación,mutex
se establece en falso, de modo que el próximo evento de entrada del usuario pasará por el control mutex por adelantado y realizará otra recuperación ...Un par de notas sobre el prototipo.
simulateFetchOfData
función, hay suspensión (100) configurada como una Promesa que simula el retraso en la recuperación de los datos. Esto se intercala con algunos registros en la consola. Si elimina la verificación de mutex, verá con la consola abierta que, mientras mueve los controles deslizantes,simulateFetchOfData
se inician muchas instancias y se ponen en suspenso esperando el sueño (es decir, la búsqueda simulada de datos) para resolver, mientras que con la verificación de mutex en su lugar, solo se inicia una instancia a la vez.mutex
a falso, se realiza una verificación para determinar si los valores de desplazamiento horizontal y vertical están alineados. Si no, se inicia otra búsqueda. Esto garantiza que, a pesar de que varios eventos de desplazamiento posiblemente no se activen debido a que la búsqueda está ocupada, como mínimo los valores de desplazamiento finales se abordan activando una búsqueda final.buffer
se utiliza para contener los datos "recuperados". Examinarlo en la consola revelará muchas entradas "vacías x XXXX". LasimulateFetchOfData
función está configurada de tal manera que si los datos ya están almacenadosbuffer
, no se realiza ninguna "búsqueda".(Para ver el prototipo, simplemente copie y pegue todo el código en un nuevo archivo de texto, cambie el nombre a ".html" y ábralo en un navegador. EDITAR: Se ha probado en Chrome y Edge).
Una vez más, este es un prototipo para demostrar un medio para limitar una acumulación de llamadas de datos innecesarias. Si esto se refactorizara para fines de producción, muchas áreas requerirán abordaje, incluyendo: 1) reducir el uso del espacio variable global; 2) agregar etiquetas de fila y columna; 3) agregar botones a los controles deslizantes para desplazarse por filas o columnas individuales; 4) posiblemente almacenando datos relacionados con el búfer, si se requieren cálculos de datos; 5) etc.
fuente
Hay algunas cosas que podrían hacerse. Lo veo como una capa intermedia de dos niveles colocada entre el procedimiento de solicitud de datos y el evento de desplazamiento del usuario.
1. Retraso en el procesamiento del evento de desplazamiento
Tienes razón, debounce no es nuestro amigo en los problemas relacionados con el desplazamiento. Pero existe la manera correcta de reducir el número de disparos.
Utilice la versión acelerada del controlador de eventos de desplazamiento que se invocará como máximo una vez por cada intervalo fijo. Puede usar el acelerador lodash o implementar su propia versión [ 1 ], [ 2 ], [ 3 ]. Establezca 40 - 100 ms como un valor de intervalo. También deberá establecer la
trailing
opción para que se procese el último evento de desplazamiento independientemente del intervalo del temporizador.2. Flujo de datos inteligente
Cuando se invoca el controlador de eventos de desplazamiento, se debe iniciar el proceso de solicitud de datos. Como mencionó, hacerlo cada vez que ocurre un evento de desplazamiento (incluso si hemos terminado con la aceleración) puede causar retrasos en el tiempo. Puede haber algunas estrategias comunes: 1) no solicite los datos si hay otra solicitud pendiente; 2) solicitar los datos no más de una vez por algún intervalo; 3) cancelar la solicitud pendiente anterior.
El primer y el segundo enfoque no son más que el rebote y la aceleración en el nivel del flujo de datos. El rebote podría implementarse con un esfuerzo mínimo con una sola condición antes de iniciar la solicitud + una solicitud adicional al final. Pero creo que el acelerador es más apropiado desde el punto de vista de UX. Aquí deberás proporcionar algo de lógica, y no te olvides de la
trailing
opción, ya que debería estar en el juego.El último enfoque (la cancelación de la solicitud) también es compatible con UX pero menos cuidadoso que el de estrangulamiento. De todos modos, inicia la solicitud, pero descarta su resultado si se inició otra solicitud después de esta. También puede intentar cancelar la solicitud si está utilizando
fetch
.En mi opinión, la mejor opción sería combinar las estrategias (2) y (3), por lo que solicita los datos solo si ha transcurrido un intervalo de tiempo fijo desde el inicio de la solicitud anterior Y cancela la solicitud si se inició otra después de .
fuente
No existe un algoritmo específico que responda a esta pregunta, pero para que no se acumule demora, debe asegurarse de dos cosas:
1. No hay fugas de memoria
Asegúrese absolutamente de que nada en su aplicación esté creando nuevas instancias de objetos, clases, matrices, etc. La memoria debe ser la misma después de desplazarse durante 10 segundos como lo es durante 60 segundos, etc. Puede asignar previamente estructuras de datos si necesita (incluidas las matrices) y luego reutilizarlas:
2. Reutilización constante de las estructuras de datos.
Esto es común en las páginas de desplazamiento infinito. En una galería de imágenes de desplazamiento infinito que muestra como máximo 30 imágenes en pantalla al mismo tiempo, en realidad puede haber solo 30-40
<img>
elementos que se crean. Estos se usan y se reutilizan a medida que los usuarios se desplazan, de modo que no es necesario crear nuevos elementos HTML (o destruirlos, y por lo tanto recolectar basura). En cambio, estas imágenes obtienen nuevas URL de origen y nuevas posiciones, y el usuario puede seguir desplazándose, pero (sin saberlo) siempre ven los mismos elementos DOM una y otra vez.Si está utilizando un lienzo, no utilizará elementos DOM para mostrar estos datos, pero la teoría es la misma, solo las estructuras de datos son suyas.
fuente