setTimeout, clearTimeout
Els temporitzadors a JavaScript, i en concret setTimeout, són una part fonamental de la seva naturalesa asíncrona. No "pausen" el codi, sinó que planifiquen l'execució d'una funció per a més tard.
Com funciona: L'event loop
Aquesta és la part més important i la que sovint es malinterpreta. JavaScript, en si mateix, té un sol fil d'execució (és single-threaded). Això vol dir que només pot fer una cosa a la vegada. Llavors, com pot "esperar" 2 segons sense bloquejar tota la resta?
No ho fa. Ho delega a l'entorn (el navegador o Node.js).
-
Crida a
setTimeout: Quan cridessetTimeout(laMevaFuncio, 2000), JavaScript no s'espera 2 segons. -
Delegació: Li diu a la Web API (una part del navegador): "Si us plau, agafa aquesta funció (
laMevaFuncio) i guarda-te-la. D'aquí a 2000 mil·lisegons, m'avises". -
Continuació: Immediatament després de donar aquesta ordre, JavaScript continua executant la següent línia de codi. No hi ha cap pausa.
-
La cua de tasques (Task Queue): Quan passen els 2000ms, la Web API mou
laMevaFuncioa una llista d'espera anomenada "Cua de tasques" (Task Queue). -
El bucle d'esdeveniments (Event Loop): Aquest és el vigilant. El seu treball és comprovar constantment dues coses:
-
La Pila d'Execució (Call Stack) està buida? (És a dir, el codi principal ha acabat?)
-
Hi ha alguna cosa a la Cua de Tasques?
-
-
Execució: Només quan la pila d'execució està buida, l'Event Loop agafa la primera funció de la Cua (
laMevaFuncio) i la posa a la Pila perquè s'executi.
Sintaxi
La sintaxi bàsica és:
setTimeout(callback, delayEnMs, ...argumentsOpcionals);
-
callback: La funció que vols executar. -
delayEnMs: El temps (en mil·lisegons) que ha d'esperar com a mínim. -
...argumentsOpcionals: Valors que vols passar a la teva funciócallback.
Exemple 1: La prova d'asincronia
Això demostra que el codi no es bloqueja.
console.log("Inici 🚀");
// Planifiquem una tasca per d'aquí a 2 segons
setTimeout(() => {
console.log("Això s'executa DESPRÉS de 2 segons");
}, 2000);
console.log("Final 🏁");
// Sortida a la consola:
// 1. "Inici 🚀"
// 2. "Final 🏁"
// ... (passa el temps) ...
// 3. "Això s'executa DESPRÉS de 2 segons"
Exemple 2: Passar arguments
function saludar(nom, cognom) {
console.log(`Hola, ${nom} ${cognom}!`);
}
// Passem "Anna" i "Solé" com a arguments a la funció 'saludar'
setTimeout(saludar, 1500, "Anna", "Solé");
// Sortida després de 1.5 segons:
// "Hola, Anna Solé!"
Avantatges
-
No bloquejant (Asincronia): És l'avantatge principal. Permet que tasques llargues (com esperar una resposta d'un servidor) o tasques diferides (com un temporitzador) s'executin sense congelar la pàgina web. L'usuari pot seguir fent clic i interactuant amb la interfície.
-
Execució diferida: Permet executar codi després d'un cert temps, útil per a notificacions, animacions, o tancar un missatge d'èxit automàticament.
-
Base per a patrons avançats: És la base per a tècniques com debounce (esperar que l'usuari deixi d'escriure per buscar) i throttle (limitar la freqüència d'execució d'una funció).
Inconvenients
1. La "Garantia Mínima" (El gran inconvenient)
El delay (per exemple, 2000ms) no és un temps exacte, és un temps mínim. setTimeout garanteix que la teva funció no s'executarà abans de 2000ms. Però podria executar-se després.
Per què? Recorda l'Event Loop. Si quan passen els 2 segons, la Pila d'Execució (el fil principal) està ocupada executant un altre codi molt llarg i pesat, la teva funció del setTimeout haurà d'esperar a la Cua de Tasques fins que el fil principal acabi.
2. El context this
Compte amb el valor de this dins del callback, especialment si no fas servir funcions de fletxa.
const usuari = {
nom: "Pau",
saludarClassic: function() {
// AQUÍ 'this' es perd. 'this' serà 'window' (al navegador).
setTimeout(function() {
console.log(`Hola, sóc ${this.nom}`); // Sortida: "Hola, sóc undefined"
}, 1000);
},
saludarModern: function() {
// Les funcions de fletxa (=>) hereten el 'this' del seu context pare.
setTimeout(() => {
console.log(`Hola, sóc ${this.nom}`); // Sortida: "Hola, sóc Pau"
}, 1000);
}
};
usuari.saludarClassic();
usuari.saludarModern();
Com cancel·lar un temporitzador: clearTimeout
setTimeout retorna un ID numèric únic. Pots utilitzar aquest ID per cancel·lar el temporitzador abans que s'executi, fent servir clearTimeout.
console.log("Missatge d'autodestrucció en 5 segons...");
// Guardem l'ID del temporitzador
const bombaId = setTimeout(() => {
console.log("BOOM! 💥");
}, 5000);
// Per exemple, si l'usuari fa clic en un botó "Desactivar"
// podem cancel·lar-lo. Aquí ho simulem amb un altre temporitzador:
setTimeout(() => {
clearTimeout(bombaId);
console.log("Bomba desactivada. Uf! 😅");
}, 3000);
// Sortida:
// 1. "Missatge d'autodestrucció en 5 segons..."
// 2. (als 3 segons) "Bomba desactivada. Uf! 😅"
// (El "BOOM! 💥" mai apareixerà)
Altres punts importants
setTimeout(fn, 0)
Què passa si poses un temps de 0? S'executa immediatament? No. Significa "posa aquesta funció a la Cua de Tasques per executar-la tan aviat com la Pila d'Execució actual estigui buida". Això és útil per "deixar respirar" el navegador (permetre que actualitzi la pantalla o gestioni clics) abans d'executar la següent tasca.
console.log("Un");
setTimeout(() => {
console.log("Dos (del timeout 0)");
}, 0);
console.log("Tres");
// Sortida:
// 1. "Un"
// 2. "Tres"
// 3. "Dos (del timeout 0)"
I setInterval? Spoiler
-
setTimeout(fn, 3000): Executafnun cop, després de 3 segons. -
setInterval(fn, 3000): Executafncada 3 segons, repetidament, fins que ho paris ambclearInterval.