En un side project que realizaba el fin de semana pasado, tuve la necesidad de comparar 10 000 elementos dentro de un arreglo en un script de JS y la interfaz se congelaba.
Logré presenciar con toda claridad la finalidad de las funciones asíncronas y compartiré contigo este 'abrir de ojos'.
Problema
Digamos que quiero remover los duplicados de 10 000 objetos al darle a un botón y que vaya mostrando el proceso en pantalla, este seria el código:
Como se puede apreciar, tiene un contador que va actualizando la interfaz; creerías que cada vez que realiza una iteración va a reflejarse un incremento en el DOM; pero mira lo que realmente ocurre si le das al botón del ejemplo:
El número salta de 0 a 10 000, el botón se congela un poco para una tarea super pesada.
Esto es debido a que JS es single-threaded y mientras esta ejecutando la tarea super pesada y cara no puede actualizar la interfaz hasta que terminé de comparar los 10 000 elementos.
Pero hay una solución que da la claridad total de porque las tareas asíncronas son tan importantes y es agregar un setTimeout de 0 segundos partiendo la tarea en pedazos mas pequeños.
El loop interno lo volví una función (nota que use arrow function para no perder el contexto) y la ejecuto cada 0 segundos en el callback del setTimeout
Ve como se comporta ahora:
Esto se da porque el runtime de JS trabaja con un event loop y al partir el loop interno en una función, cada iteración la va colocando en el call stack (si notas esa promesa por dentro sigue usando el callback.. )
Sí conocía el event loop y por teoría dominaba el concepto; pero presenciarlo es muy diferente.
Investigué un poco viendo cómo funciona en lenguajes como C# que manejan multiples threads y es similar; por tener multiples threads no significa que va a pasarlo a otro; ya que la idea es no bloquear el thread con la tarea asíncrona.
Si desean ampliar lo que menciono aquí, les dejo unas referencias