A la lliçó anterior vas aconseguir que el programa decidís. El problema és que decidia sobre una sola tasca: per classificar la urgència de les sis tasques del backlog de Taller Nómada hauries de copiar el mateix bloc sis vegades, i quan la Marta hi afegeixi la setena caldria tornar a tocar el codi. Un bucle resol exactament això: escrius les instruccions una vegada i li dius al programa quantes vegades les ha de repetir. En aquesta lliçó coneixeràs els tres bucles clàssics de JavaScript —for, while i do...while—, aprendràs a recórrer una llista de tasques per índex i dominaràs els quatre patrons que resolen el 90 % dels problemes reals: comptar, acumular, buscar el màxim i comprovar si existeix alguna cosa.

Contingut

  1. Què és un bucle i per què el necessites
  2. Anatomia del bucle for
  3. El bucle while
  4. El bucle do...while
  5. Quin triar: taula comparativa
  6. Recórrer una llista per índex amb .length
  7. El backlog de Taller Nómada
  8. Patró acumulador: hores totals del backlog
  9. Patró comptador: quantes tasques porta cada persona
  10. Patró màxim/mínim: la tasca més urgent
  11. Patró bandera: hi ha alguna tasca vençuda?
  12. Bucles infinits i com evitar-los
  13. for...of i for...in: un avançament
  14. Errors Habituals i Consells
  15. Exercicis
  16. Conclusió

  1. Què és un bucle i per què el necessites

Un bucle (o iteració) és una estructura de control que executa un bloc de sentències repetidament mentre es compleixi una condició. Cada passada completa pel bloc s'anomena iteració.

Sense bucles, el resum del backlog s'escriu així:

// ✗ Insostenible
console.log(`1. ${titol1} · ${hores1} h`);
console.log(`2. ${titol2} · ${hores2} h`);
console.log(`3. ${titol3} · ${hores3} h`);
// ...i així fins a quaranta

Amb un bucle s'escriu una vegada i funciona amb sis tasques, amb quaranta o amb cap. A més, si demà cal afegir la prioritat al resum, canvies una línia en lloc de quaranta.

  1. Anatomia del bucle for

El for és el bucle de referència quan saps per endavant quantes vegades cal repetir o quan recorres una llista de principi a fi. La seva capçalera conté tres parts separades per ;:

for (inicialitzacio; condicio; actualitzacio) {
  // cos
}

Un exemple mínim: imprimir els tres primers números de tasca.

for (let i = 1; i <= 3; i++) {
  console.log(`Processant la tasca número ${i}`);
}

Sortida:

Processant la tasca número 1
Processant la tasca número 2
Processant la tasca número 3

Què fa cada part, i en quin ordre:

Part Quan s'executa A l'exemple
Inicialització Una sola vegada, abans de tot let i = 1
Condició Abans de cada iteració i <= 3
Cos Si la condició ha estat certa console.log(...)
Actualització Després de cada iteració i++

El flux complet, pas a pas:

flowchart TD
    A["Inicialització<br/>let i = 1"] --> B{"Condició<br/>i <= 3"}
    B -->|false| F["Fi del bucle"]
    B -->|true| C["Cos<br/>console.log(...)"]
    C --> D["Actualització<br/>i++"]
    D --> B

Segueix la traça mentalment:

Pas i Condició i <= 3 Què passa
1 1 true Imprimeix "1", després i passa a 2
2 2 true Imprimeix "2", després i passa a 3
3 3 true Imprimeix "3", després i passa a 4
4 4 false El bucle acaba

Dues observacions fonamentals:

  • La condició es comprova ABANS de cada iteració, inclosa la primera. Si comences amb let i = 5 i la condició és i <= 3, el cos no s'executa ni una sola vegada. Això és correcte i desitjable: un bucle sobre una llista buida ha de fer zero iteracions.
  • L'actualització s'executa DESPRÉS del cos, no abans. Per això a la primera iteració i encara val 1.

2.1 La variable de control

Per convenció s'anomena i (d'índex), i j, k si n'hi ha més. Es declara amb let, mai amb const: ha de poder canviar a cada actualització.

// ✗ TypeError: Assignment to constant variable
for (const i = 0; i < 3; i++) { }

Declarar-la a la capçalera amb let fa que només existeixi dins del bucle, que és exactament el que vols:

for (let i = 0; i < 3; i++) {
  console.log(i);
}
console.log(i);   // ✗ ReferenceError: i is not defined

Si necessites conèixer el valor final del comptador després del bucle, declara la variable fora. Però gairebé mai no cal: és senyal que en realitat volies un while.

2.2 Variants de la capçalera

Les tres parts són opcionals, encara que els ; no. Aquestes variants són legals:

let i = 0;
for (; i < 3; i++) { }        // sense inicialització

for (let j = 10; j > 0; j -= 2) { }   // compte enrere de 2 en 2

for (let k = 0, total = 0; k < 3; k++) { }   // dues variables, separades per coma

Comptar cap enrere és habitual quan l'ordre importa:

for (let i = 5; i >= 1; i--) {
  console.log(`Queden ${i} tasques per revisar`);
}

  1. El bucle while

while repeteix un bloc mentre una condició sigui certa. No té inicialització ni actualització pròpies: te n'encarregues tu.

let horesPendents = 45;
let dies = 0;
const HORES_PER_DIA = 8;

while (horesPendents > 0) {
  horesPendents -= HORES_PER_DIA;
  dies++;
}

console.log(`El backlog obert ocupa ${dies} dies de feina.`);
// El backlog obert ocupa 6 dies de feina.

És exactament el mateix mecanisme que un for amb les tres parts repartides:

let i = 0;                 // inicialització: fora
while (i < 3) {            // condició: a la capçalera
  console.log(i);
  i++;                     // actualització: última línia del cos
}

Aleshores, per a què serveix while si for fa el mateix? Per als casos en què no saps quantes iteracions caldran. A l'exemple de les hores no sabem per endavant quants dies en sortiran: depèn del total. Escriure-ho amb for obligaria a calcular abans el nombre d'iteracions, que és justament el que volíem esbrinar.

  1. El bucle do...while

do...while és idèntic a while amb una diferència decisiva: el cos s'executa almenys una vegada, perquè la condició es comprova al final.

let intents = 0;
let tascaTrobada = false;

do {
  intents++;
  console.log(`Intent de sincronització núm. ${intents}`);
  tascaTrobada = intents === 3;   // simulem que a la tercera hi surt la vençuda
} while (!tascaTrobada && intents < 5);

console.log(`Sincronitzat després de ${intents} intent/s.`);

Fixa't en el ; final: a do...while és obligatori després del while, perquè la sentència acaba aquí. És l'únic dels tres que en porta.

La diferència amb while es veu amb una condició falsa des del principi:

let n = 10;

while (n < 5) {
  console.log('while: no executo mai el cos');
}

do {
  console.log('do...while: executo el cos una vegada');
} while (n < 5);

Sortida:

do...while: executo el cos una vegada
flowchart LR
    subgraph W["while"]
      direction TB
      W1{"Condició?"} -->|true| W2["Cos"]
      W2 --> W1
      W1 -->|false| W3["Fi"]
    end
    subgraph D["do...while"]
      direction TB
      D1["Cos"] --> D2{"Condició?"}
      D2 -->|true| D1
      D2 -->|false| D3["Fi"]
    end

do...while és el menys utilitzat dels tres. El seu nínxol són els processos que sempre s'han d'intentar una vegada i després decidir si es repeteixen: demanar una dada fins que sigui vàlida, reintentar una operació fallida, generar un identificador fins que no estigui repetit.

  1. Quin triar: taula comparativa

Criteri for while do...while
Nre. d'iteracions conegut ✓ Ideal △ Possible ✗ Rar
Nre. d'iteracions desconegut △ Possible ✓ Ideal ✓ Si va almenys una vegada
El cos s'executa com a mínim 0 vegades 0 vegades 1 vegada
La condició es comprova Al principi Al principi Al final
Comptador visible a la capçalera ✓ Sí ✗ No ✗ No
Recórrer una llista completa ✓ L'opció natural △ Verbós ✗ Falla amb llista buida
Risc de bucle infinit Baix Mitjà Mitjà
Necessita ; final No No

Regla pràctica: si estàs recorrent una llista o repetint un nombre conegut de vegades, for. Si repeteixes "fins que passi alguna cosa", while. Si a més aquesta cosa només es pot avaluar després del primer intent, do...while.

  1. Recórrer una llista per índex amb .length

Per treballar amb el backlog necessites desar diversos valors en una sola variable. Això és un array: una llista ordenada de valors, escrita entre claudàtors i separada per comes.

const titols = [
  'Redissenyar la sala polivalent',
  'Cartelleria del taller de serigrafia',
  'Actualitzar el web de reserves'
];

De moment només necessites tres coses dels arrays; la resta arriba a Arrays: Conceptes Bàsics i Mètodes:

Necessito S'escriu Exemple
Quants elements hi ha array.length titols.length3
L'element a la posició N array[N] titols[0]'Redissenyar la sala polivalent'
Recórrer-los tots Un for amb índex for (let i = 0; i < titols.length; i++)

Els índexs comencen a 0. Un array de 3 elements té els índexs 0, 1 i 2. L'últim índex és sempre length - 1, i array[length] dona undefined, no un error. Aquesta és la causa número u d'errors amb bucles.

for (let i = 0; i < titols.length; i++) {
  console.log(`${i + 1}. ${titols[i]}`);
}

Sortida:

1. Redissenyar la sala polivalent
2. Cartelleria del taller de serigrafia
3. Actualitzar el web de reserves

Aquesta capçalera —let i = 0, i < array.length, i++— és el patró més repetit de tot JavaScript. Interioritza-la:

  • Comença a 0 perquè és el primer índex.
  • Fa servir < i no <=, perquè length és el nombre d'elements, no l'últim índex.
  • L'i + 1 del missatge és només cosmètic: converteix l'índex en un número d'ordre llegible per a la Marta.

  1. El backlog de Taller Nómada

Encara no saps crear objectes, així que representarem les sis tasques amb arrays paral·lels: un array per camp, on la posició i de tots ells descriu la mateixa tasca. Aquest codi serà el punt de partida dels exemples següents i dels de les lliçons 02-03 i 02-04.

const AVUI = '2026-09-20';

const ids          = [1, 2, 3, 4, 5, 6];
const titols       = [
  'Redissenyar la sala polivalent',
  'Cartelleria del taller de serigrafia',
  'Actualitzar el web de reserves',
  'Inventari de tintes de serigrafia',
  'Guia sobre enquadernació per a residents',
  'Pressupost de la fusteria'
];
const responsables = ['Iván', 'Marta', 'Lucía', 'Marta', 'Iván', 'Iván'];
const prioritats   = ['alta', 'mitjana', 'alta', 'baixa', 'mitjana', 'alta'];
const estats       = ['en-curs', 'pendent', 'pendent', 'feta', 'en-curs', 'pendent'];
const hores        = [12, 6, 14, 3, 8, 5];
const datesLimit   = [
  '2026-09-30', '2026-10-15', '2026-10-02',
  '2026-09-12', '2026-11-05', '2026-09-05'
];

Llegir la tasca número 3 significa mirar la posició 2 de tots els arrays: titols[2], responsables[2], hores[2]... Així queda el llistat complet:

i id Títol Responsable Prioritat Estat Hores Data límit
0 1 Redissenyar la sala polivalent Iván alta en-curs 12 2026-09-30
1 2 Cartelleria del taller de serigrafia Marta mitjana pendent 6 2026-10-15
2 3 Actualitzar el web de reserves Lucía alta pendent 14 2026-10-02
3 4 Inventari de tintes de serigrafia Marta baixa feta 3 2026-09-12
4 5 Guia sobre enquadernació per a residents Iván mitjana en-curs 8 2026-11-05
5 6 Pressupost de la fusteria Iván alta pendent 5 2026-09-05

Els arrays paral·lels són una solució provisional i fràgil. Si n'ordenes un i els altres no, les dades queden descol·locades sense previ avís. La representació correcta és un array d'objectes, i arribarà al Mòdul 4. Mentrestant serveixen perfectament per aprendre bucles, i patir-ne la fragilitat et farà valorar els objectes quan arribin.

Un llistat complet del backlog combinant bucle i condicional:

for (let i = 0; i < ids.length; i++) {
  const marca = estats[i] === 'feta' ? '✓' : '○';
  const alerta = datesLimit[i] < AVUI && estats[i] !== 'feta' ? ' ⚠ VENÇUDA' : '';
  console.log(
    `${marca} [${ids[i]}] ${titols[i]} · ${responsables[i]} · ${prioritats[i]} · ${hores[i]} h${alerta}`
  );
}

Sortida:

○ [1] Redissenyar la sala polivalent · Iván · alta · 12 h
○ [2] Cartelleria del taller de serigrafia · Marta · mitjana · 6 h
○ [3] Actualitzar el web de reserves · Lucía · alta · 14 h
✓ [4] Inventari de tintes de serigrafia · Marta · baixa · 3 h
○ [5] Guia sobre enquadernació per a residents · Iván · mitjana · 8 h
○ [6] Pressupost de la fusteria · Iván · alta · 5 h ⚠ VENÇUDA

Ja tens les dues estructures del mòdul treballant juntes: el bucle recorre, el condicional decideix. La tasca 4 no es marca com a vençuda tot i que la seva data ja ha passat, perquè està feta: la regla R10 exigeix totes dues condicions.

  1. Patró acumulador: hores totals del backlog

L'acumulador és una variable declarada abans del bucle que va sumant (o concatenant) a cada iteració.

let totalHores = 0;
let horesObertes = 0;

for (let i = 0; i < hores.length; i++) {
  totalHores += hores[i];
  if (estats[i] !== 'feta') {
    horesObertes += hores[i];
  }
}

console.log(`Hores estimades totals:  ${totalHores} h`);   // 48 h
console.log(`Hores de feina oberta:   ${horesObertes} h`); // 45 h

Els tres elements del patró:

  1. Inicialitzar fora del bucle, i amb el valor neutre: 0 per a sumes, 1 per a productes, '' per a textos. Si el declares a dins, es reinicia a cada iteració i el resultat és sempre l'últim valor.
  2. Acumular a dins, amb +=.
  3. Fer-lo servir després, quan el bucle ha acabat.

Declarar-lo amb let és obligatori: const impediria reassignar-lo.

Un acumulador amb condició —com horesObertes— és la base de tots els informes de la Marta: sumar només allò que compleix un criteri. Al Mòdul 4 això s'escriurà en una línia amb reduce, però el mecanisme que aprens aquí és exactament el mateix.

  1. Patró comptador: quantes tasques porta cada persona

Un comptador és un acumulador que suma 1 en lloc d'un valor. Amb tres persones a l'equip n'hi ha prou amb tres comptadors i una cadena else if:

let tasquesIvan = 0;
let tasquesMarta = 0;
let tasquesLucia = 0;

for (let i = 0; i < responsables.length; i++) {
  if (responsables[i] === 'Iván') {
    tasquesIvan++;
  } else if (responsables[i] === 'Marta') {
    tasquesMarta++;
  } else if (responsables[i] === 'Lucía') {
    tasquesLucia++;
  } else {
    console.warn(`Responsable no reconegut a la tasca ${ids[i]}: ${responsables[i]}`);
  }
}

console.log(`Iván:  ${tasquesIvan} tasques`);   // Iván:  3 tasques
console.log(`Marta: ${tasquesMarta} tasques`);  // Marta: 2 tasques
console.log(`Lucía: ${tasquesLucia} tasques`);  // Lucía: 1 tasques

El repartiment confirma el diagnòstic de la lliçó 01-08: l'Iván està sobrecarregat i la Lucía té marge. Exactament el tipus de dada que la Marta no podia veure amb la seva paret de notes adhesives.

Aquest codi fa una olor evident: tres variables gairebé idèntiques i una cadena d'else if que creix amb cada persona nova. Si demà entra algú a l'equip, cal tocar quatre llocs. La solució neta són els bucles imbricats sobre un array de responsables (lliçó 02-04) i, definitivament, un objecte comptador al Mòdul 4. Reconèixer la incomoditat ara forma part de l'aprenentatge.

Combinant comptador i acumulador obtenim la càrrega real de l'Iván, necessària per a la regla R7 (ningú no supera 40 h):

let horesIvan = 0;

for (let i = 0; i < responsables.length; i++) {
  if (responsables[i] === 'Iván' && estats[i] !== 'feta') {
    horesIvan += hores[i];
  }
}

console.log(`Càrrega oberta de l'Iván: ${horesIvan} h de 40`);   // 25 h de 40

if (horesIvan > 40) {
  console.error('R7 incomplerta: Iván supera les 40 hores setmanals.');
} else {
  console.log(`Marge disponible: ${40 - horesIvan} h`);          // Marge disponible: 15 h
}

  1. Patró màxim/mínim: la tasca més urgent

Per trobar el valor extrem d'una llista es desa un candidat provisional i se substitueix cada vegada que n'apareix un de millor. Busquem la tasca oberta amb la data límit més propera:

let dataMesPropera = '';
let titolMesUrgent = '';
let idMesUrgent = 0;

for (let i = 0; i < datesLimit.length; i++) {
  const esCandidata = estats[i] !== 'feta';
  const esLaPrimera = dataMesPropera === '';
  const vencAbans = datesLimit[i] < dataMesPropera;

  if (esCandidata && (esLaPrimera || vencAbans)) {
    dataMesPropera = datesLimit[i];
    titolMesUrgent = titols[i];
    idMesUrgent = ids[i];
  }
}

console.log(`Més urgent: [${idMesUrgent}] ${titolMesUrgent} (venç ${dataMesPropera})`);
// Més urgent: [6] Pressupost de la fusteria (venç 2026-09-05)

Les tres claus del patró:

  • El valor inicial ha de ser impossible o marcar "encara no hi ha candidat". Aquí fem servir '', i la variable esLaPrimera detecta aquest cas. L'alternativa habitual —inicialitzar amb el primer element— no serveix aquí perquè el primer element podria estar fet i quedar descartat.
  • La comparació de dates ISO com a text funciona, com ja saps des de la lliçó 01-08.
  • Cal arrossegar totes les dades del guanyador alhora. Com que els camps són en arrays paral·lels, en trobar un candidat millor cal copiar-ne la data, el títol i l'id al mateix bloc. Oblidar-ne un produeix un resultat barrejat: la data d'una tasca amb el títol d'una altra. És la fragilitat dels arrays paral·lels en acció.

Per al màxim el patró és idèntic invertint la comparació. La tasca amb més hores:

let maxHores = -1;
let titolMesLlarg = '';

for (let i = 0; i < hores.length; i++) {
  if (hores[i] > maxHores) {
    maxHores = hores[i];
    titolMesLlarg = titols[i];
  }
}

console.log(`La més llarga: ${titolMesLlarg} (${maxHores} h)`);
// La més llarga: Actualitzar el web de reserves (14 h)

-1 com a valor inicial és segur perquè la regla R3 garanteix que cap tasca no té hores negatives. Inicialitzar a 0 també funcionaria, però -1 deixa clar que és un sentinella i no una dada.

  1. Patró bandera: hi ha alguna tasca vençuda?

Una bandera (flag) és una variable booleana que comença en false i es posa a true tan bon punt passa el que busques. Serveix per respondre preguntes de sí o no sobre tota la llista.

let hiHaVencudes = false;
let quantesVencudes = 0;

for (let i = 0; i < datesLimit.length; i++) {
  if (datesLimit[i] < AVUI && estats[i] !== 'feta') {
    hiHaVencudes = true;
    quantesVencudes++;
  }
}

if (hiHaVencudes) {
  console.error(`Atenció: ${quantesVencudes} tasca/es vençuda/es al tauler.`);
} else {
  console.log('Cap tasca vençuda. Tot en termini.');
}
// Atenció: 1 tasca/es vençuda/es al tauler.

La bandera contrària —comprovar que totes compleixen alguna cosa— s'inicialitza al revés, en true, i es posa a false tan bon punt apareix un contraexemple:

let totesAssignades = true;

for (let i = 0; i < responsables.length; i++) {
  if (responsables[i] === null) {
    totesAssignades = false;
  }
}

console.log(totesAssignades ? 'Totes les tasques tenen responsable.' : 'Hi ha tasques sense assignar.');
Pregunta Valor inicial Es canvia a Quan
Existeix alguna que compleixi X? false true En trobar la primera que compleix
Compleixen totes X? true false En trobar la primera que no compleix

En tots dos casos el bucle continua recorrent la llista encara que la resposta ja es conegui. Això és feina malgastada, i a la lliçó 02-04 aprendràs a tallar en sec amb break.

  1. Bucles infinits i com evitar-los

Un bucle infinit és aquell la condició del qual no arriba mai a ser falsa. Al navegador la pestanya es congela; a Node.js el procés es queda penjat fins que el mates amb Ctrl + C.

Les tres causes habituals:

Oblidar l'actualització.

// ✗ i val 0 per sempre
let i = 0;
while (i < 6) {
  console.log(titols[i]);
}

Actualitzar en la direcció equivocada.

// ✗ i creix, la condició demana que baixi
for (let i = 6; i > 0; i++) { }

Modificar el comptador dins del cos sense adonar-te'n.

// ✗ i torna a 0 a la primera tasca sense assignar i no en surt mai
for (let i = 0; i < responsables.length; i++) {
  if (responsables[i] === null) {
    i = 0;
  }
}

Com protegir-te:

  • Comprova abans d'executar que la condició pot arribar a ser falsa. Localitza quina variable apareix a la condició i verifica que el cos o l'actualització la mouen cap al final.
  • No modifiquis la variable de control dins del cos. Si creus que ho necessites, gairebé segur que volies un while.
  • Afegeix un límit de seguretat en bucles la condició dels quals depèn de dades externes:
let intents = 0;
const MAX_INTENTS = 1000;

while (!hiHaVencudes && intents < MAX_INTENTS) {
  intents++;
  // ...
}

if (intents === MAX_INTENTS) {
  console.error('Límit de seguretat assolit: revisa la condició del bucle.');
}
  • Si ja has penjat la pestanya, tanca-la i fes servir node fitxer.js amb Ctrl + C per provar bucles dubtosos: és molt més fàcil d'aturar.

Hi ha una excepció legítima: while (true) amb una sortida explícita a dins. Es fa servir en bucles de reintent i sempre porta un break, que veuràs a la lliçó vinent.

  1. for...of i for...in: un avançament

JavaScript modern té dos bucles més, pensats per recórrer col·leccions sense gestionar l'índex a mà. Els presentem aquí perquè els reconeguis; el detall sobre arrays és a Iteració sobre Arrays.

for...of recorre els valors d'un array (o de qualsevol cosa iterable, com un text):

for (const titol of titols) {
  console.log(titol);
}

Més net que el for clàssic quan només necessites el valor i no la posició. Fixa't que la variable pot ser const, perquè a cada iteració es crea de nou. El problema en el nostre cas és que amb arrays paral·lels sí que necessitem l'índex per consultar els altres camps, així que continuarem fent servir el for clàssic durant aquest mòdul.

for...in recorre les claus (en un array, els índexs, i a més com a text):

for (const i of titols) { }   // valors
for (const i in titols) {     // índexs, però '0', '1', '2'... com a strings
  console.log(i + 1);         // ✗ '01', '11', '21' — concatena, no suma
}

Aquest parany de la concatenació és el motiu principal de la norma universal: for...in és per a objectes, no per a arrays. Recorre també propietats heretades i no en garanteix l'ordre. Quan arribis als objectes al Mòdul 4 li trobaràs el seu lloc.

Bucle Què et dona Fes-lo servir per a
for (let i = 0; ...) L'índex numèric Arrays quan necessites la posició
for...of El valor Arrays i textos quan només vols el valor
for...in La clau com a text Objectes, no arrays

Errors Habituals i Consells

Fer servir <= amb .length.

for (let i = 0; i <= titols.length; i++) { }   // ✗ una iteració de més

A l'última volta titols[6] val undefined, i el resum imprimeix "undefined · undefined". Amb .length va sempre <.

Començar a 1. for (let i = 1; i < titols.length; i++) es salta la primera tasca silenciosament. Els índexs comencen a 0.

Declarar l'acumulador dins del bucle.

for (let i = 0; i < hores.length; i++) {
  let total = 0;        // ✗ es reinicia a cada volta
  total += hores[i];
}

L'acumulador val sempre el mateix que l'últim element, i a més desapareix en sortir. Va abans del bucle.

Confondre l'índex amb el valor. hores[i] és el nombre d'hores; i és la posició. totalHores += i suma 0+1+2+3+4+5 = 15, un nombre que sembla plausible i no ho és. Quan un total surti estrany, comprova això primer.

Modificar .length dins del bucle. Afegir o treure elements mentre recorres canvia la condició sobre la marxa i produeix salts o bucles infinits. Recorre per llegir; construeix la llista modificada a part.

Oblidar el ; de do...while. És l'únic que en porta, i l'ASI que vas veure a la lliçó 01-04 no sempre et salva.

Consell: posa un bon nom a la variable de control. i està acceptat universalment per a índexs, però si el bucle és llarg, let indexTasca es llegeix millor al cap de tres mesos.

Consell: si el cos del bucle passa de vint línies, alguna cosa va malament. Al Mòdul 3 aprendràs a extreure aquesta lògica a funcions; de moment, com a mínim separa els càlculs en variables amb nom abans de l'if.

Consell: imprimeix la traça quan alguna cosa no quadri. Un console.log(i, titols[i], totalHores) com a primera línia del cos et mostra l'estat a cada volta i resol la majoria de les fallades en segons.

Exercicis

Exercici 1 — Resum del taller

Partint del backlog de la secció 7, escriu un únic bucle for que calculi i mostri:

  • El nombre total de tasques.
  • Quantes estan pendent, quantes en-curs i quantes feta.
  • El percentatge d'avenç, entès com a tasques fetes sobre el total, arrodonit a un decimal.

Recorda de la lliçó 01-06 que Math.round(x * 10) / 10 arrodoneix a un decimal.

Exercici 2 — Compte enrere de venciments

Escriu un bucle while que, partint d'horesObertes = 45 i una jornada de 8 hores, calculi quants dies laborables calen per buidar el backlog, imprimint a cada volta les hores que van quedant. Després respon: per què aquí while és millor elecció que for?

Exercici 3 — Tasca de més pes

Fent servir la conversió de prioritat a pes de la lliçó anterior (alta = 3, mitjana = 2, baixa = 1), calcula per a cada tasca oberta un índex d'esforç igual a pes * hores, i troba la tasca oberta amb l'índex més alt. Mostra el llistat complet amb el seu índex i, al final, la guanyadora.

Solucions

Exercici 1

let pendents = 0;
let enCurs = 0;
let fetes = 0;

for (let i = 0; i < estats.length; i++) {
  if (estats[i] === 'pendent') {
    pendents++;
  } else if (estats[i] === 'en-curs') {
    enCurs++;
  } else if (estats[i] === 'feta') {
    fetes++;
  } else {
    console.warn(`Estat desconegut a la tasca ${ids[i]}: ${estats[i]}`);
  }
}

const total = estats.length;
const avenc = Math.round((fetes / total) * 100 * 10) / 10;

console.log(`Total de tasques: ${total}`);         // 6
console.log(`Pendents: ${pendents}`);              // 3
console.log(`En curs:  ${enCurs}`);                // 2
console.log(`Fetes:    ${fetes}`);                 // 1
console.log(`Avenç: ${avenc} %`);                  // 16.7 %

Tres comptadors actualitzats en el mateix recorregut: recórrer la llista una sola vegada i calcular tot el que necessitis en aquella passada és sempre preferible a fer tres bucles. L'else final protegeix contra estats no previstos, aplicant la norma de la lliçó 02-01 de tancar sempre les cadenes.

El càlcul del percentatge mereix atenció: fetes / total dona 0.1666...; multiplicar per 100 dona 16.666...; i el truc de multiplicar per 10, arrodonir i dividir entre 10 deixa 16.7 com a nombre. toFixed(1) donaria el text '16.7', i si més endavant el sumessis a una altra cosa obtindries una concatenació en lloc d'una suma.

Exercici 2

let horesObertes = 45;
const HORES_PER_DIA = 8;
let dies = 0;

while (horesObertes > 0) {
  dies++;
  horesObertes -= HORES_PER_DIA;
  const restants = horesObertes > 0 ? horesObertes : 0;
  console.log(`Dia ${dies}: queden ${restants} h`);
}

console.log(`El backlog obert ocupa ${dies} dia/es laborable/s.`);

Sortida:

Dia 1: queden 37 h
Dia 2: queden 29 h
Dia 3: queden 21 h
Dia 4: queden 13 h
Dia 5: queden 5 h
Dia 6: queden 0 h
El backlog obert ocupa 6 dia/es laborable/s.

El ternari evita mostrar -3 h a l'última volta, perquè restar 8 a 5 se salta el zero.

Per què while: el nombre d'iteracions no es coneix abans de començar; és precisament el que el bucle calcula. Amb un for caldria escriure for (let d = 0; d < Math.ceil(45 / 8); d++), és a dir, resoldre el problema a la capçalera per després simular que el resolem al cos. La regla es compleix: quan la condició d'aturada depèn d'un estat que evoluciona, while.

Exercici 3

let maxEsforc = -1;
let titolGuanyadora = '';

for (let i = 0; i < ids.length; i++) {
  if (estats[i] === 'feta') {
    console.log(`[${ids[i]}] ${titols[i]} — tancada, no puntua`);
  } else {
    let pes;
    if (prioritats[i] === 'alta') {
      pes = 3;
    } else if (prioritats[i] === 'mitjana') {
      pes = 2;
    } else {
      pes = 1;
    }

    const esforc = pes * hores[i];
    console.log(`[${ids[i]}] ${titols[i]} — esforç ${esforc}`);

    if (esforc > maxEsforc) {
      maxEsforc = esforc;
      titolGuanyadora = titols[i];
    }
  }
}

console.log(`\nMés esforç: ${titolGuanyadora} (${maxEsforc})`);

Sortida:

[1] Redissenyar la sala polivalent — esforç 36
[2] Cartelleria del taller de serigrafia — esforç 12
[3] Actualitzar el web de reserves — esforç 42
[4] Inventari de tintes de serigrafia — tancada, no puntua
[5] Guia sobre enquadernació per a residents — esforç 16
[6] Pressupost de la fusteria — esforç 15

Més esforç: Actualitzar el web de reserves (42)

Tres detalls que convé notar. Primer, let pes; es declara dins del bucle a propòsit: és un valor diferent a cada iteració, no un acumulador, així que s'ha de reiniciar. Segon, maxEsforc i titolGuanyadora sí que van fora, perquè han de sobreviure entre voltes. I tercer, aquesta cadena de tres else if repetida a cada iteració és exactament el cas que resoldrà el switch de la lliçó vinent de manera més compacta.

Conclusió

Ja saps repetir. Coneixes el for amb la seva triple capçalera —inicialització, condició i actualització— i l'ordre exacte en què s'executen; el while, per quan no saps quantes voltes caldran; i el do...while, per allò que s'ha d'intentar almenys una vegada. Saps recórrer una llista amb for (let i = 0; i < array.length; i++), que és la capçalera més escrita de tot JavaScript, i per què els índexs van de 0 a length - 1.

Sobretot, domines els quatre patrons que resolen gairebé qualsevol recorregut: l'acumulador per sumar hores, el comptador per repartir tasques per persona, el màxim/mínim per trobar la més urgent i la bandera per respondre si n'hi ha alguna de vençuda. Tots quatre comparteixen la mateixa estructura: una variable declarada abans del bucle, actualitzada a dins i consultada després. I saps reconèixer i evitar un bucle infinit abans de congelar la pestanya.

Aplicat a Nómada Tasques, ja pots generar l'informe que la Marta no va tenir mai: 48 hores estimades en total, 45 de feina oberta, un 16,7 % d'avenç, una tasca vençuda i una càrrega molt desigual —l'Iván amb tres tasques i 25 hores davant de l'única de la Lucía—.

Hauràs notat dues incomoditats. Una: convertir prioritat en un pes obligava a repetir la mateixa cadena d'else if una vegada i una altra. Dues: els bucles continuen recorrent la llista sencera encara que la resposta ja es conegui. La primera s'ataca a Sentències Switch, on aprendràs una construcció pensada exactament per triar entre diversos valors d'una mateixa variable, i la segona a la lliçó següent, amb break i continue.

Curs de JavaScript: De Principiant a Avançat

Mòdul 1: Introducció a JavaScript

Mòdul 2: Estructures de Control

Mòdul 3: Funcions

Mòdul 4: Objectes i Arrays

Mòdul 5: Objectes i Funcions Avançades

Mòdul 6: El Model d'Objectes del Document (DOM)

Mòdul 7: APIs del Navegador i Temes Avançats

Mòdul 8: Proves i Depuració

Mòdul 9: Rendiment i Optimització

Mòdul 10: Frameworks i Llibreries de JavaScript

Mòdul 11: Projecte Final

© Copyright 2026. Tots els drets reservats