Els bucles que vas escriure a la lliçó 02-02 tenen un defecte: recorren la llista sencera, sempre, encara que la resposta es conegui a la primera volta. Buscar si hi ha alguna tasca vençuda entre quaranta i continuar comprovant les trenta-nou restants després de trobar-la és feina llençada. I hi ha una altra cosa que encara no saps fer: creuar dues llistes —els tres membres de l'equip contra les sis tasques del backlog— per construir el tauler que la Marta vol veure. En aquesta lliçó aprendràs a interrompre un bucle amb break, a saltar-te iteracions amb continue, a imbricar bucles i controlar-ne el cost, i a fer servir etiquetes per als casos rars en què cal sortir de diversos bucles alhora.

Contingut

  1. break: sortir del bucle tan bon punt trobes el que busques
  2. El cost de recórrer de més
  3. continue: saltar-se una iteració
  4. break i continue a while i do...while
  5. break, continue o bandera: quin fer servir
  6. Bucles imbricats
  7. El creixement del nombre d'iteracions
  8. break i continue dins de bucles imbricats
  9. Etiquetes: break etiqueta i continue etiqueta
  10. Alternatives més llegibles que les etiquetes
  11. Cas pràctic: la matriu responsable × estat
  12. Errors Habituals i Consells
  13. Exercicis
  14. Conclusió

Tots els exemples parteixen del backlog de Taller Nómada que vas definir a la lliçó 02-02, ampliat amb un camp més:

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'];
const arxivades    = [false, false, false, true, false, false];

  1. break: sortir del bucle tan bon punt trobes el que busques

break acaba el bucle immediatament. L'execució continua a la primera sentència posterior al bucle: no s'executa la resta del cos, no s'executa l'actualització i no es torna a comprovar la condició.

La Marta vol saber quina és la primera tasca de prioritat alta del tauler per començar per aquí:

let idTrobada = 0;
let titolTrobat = '';
let voltes = 0;

for (let i = 0; i < ids.length; i++) {
  voltes++;
  if (prioritats[i] === 'alta') {
    idTrobada = ids[i];
    titolTrobat = titols[i];
    break;                       // ← ja està: no cal continuar
  }
}

console.log(`Primera prioritària: [${idTrobada}] ${titolTrobat}`);
console.log(`Iteracions realitzades: ${voltes} de ${ids.length}`);

Sortida:

Primera prioritària: [1] Redissenyar la sala polivalent
Iteracions realitzades: 1 de 6

Una volta en lloc de sis. El comptador voltes només hi és per fer visible l'estalvi; en codi real no s'hi posa.

Compara-ho amb la versió sense break, que és la que sabies escriure fins ara:

// △ Correcta, però continua buscant després de trobar
for (let i = 0; i < ids.length; i++) {
  if (prioritats[i] === 'alta' && idTrobada === 0) {
    idTrobada = ids[i];
    titolTrobat = titols[i];
  }
}

Aquesta versió funciona, però necessita la condició extra idTrobada === 0 per no quedar-se amb l'última coincidència en lloc de la primera. break elimina aquesta condició artificial: expressa la intenció —"atura't aquí"— en lloc de simular-la.

El segon exemple del projecte, buscar la primera tasca vençuda:

let hiHaVencuda = false;
let titolVencuda = '';

for (let i = 0; i < ids.length; i++) {
  if (datesLimit[i] < AVUI && estats[i] !== 'feta') {
    hiHaVencuda = true;
    titolVencuda = titols[i];
    break;
  }
}

if (hiHaVencuda) {
  console.error(`⚠ Hi ha almenys una tasca vençuda: ${titolVencuda}`);
} else {
  console.log('Cap tasca vençuda.');
}
// ⚠ Hi ha almenys una tasca vençuda: Pressupost de la fusteria

Aquí el break no estalvia res, perquè l'única vençuda és justament l'última de l'array. Amb les quaranta tasques que la Marta tindrà el mes vinent, l'estalvi serà real i variable. El que sí que aporta sempre és claredat: qui llegeix el codi entén de seguida que se cerca la primera coincidència i no totes.

  1. El cost de recórrer de més

Amb sis tasques, recórrer de més no es nota: un ordinador fa milions de comparacions per segon. Aleshores, per què importa?

Perquè el cost creix amb les dades i amb la feina per iteració. Aquests són els factors:

Factor Exemple a Nómada Tasques Impacte
Nombre d'elements 6 tasques avui, 400 d'aquí a dos anys Lineal
Feina per iteració Comparar dos textos Menyspreable
Feina per iteració Pintar una fila a la pantalla Alt (Mòdul 9)
Bucles imbricats 3 responsables × 400 tasques Multiplicatiu
Efectes secundaris Enviar un avís per cada coincidència Pot duplicar avisos

L'últim factor és el més important i no té res a veure amb la velocitat: si el cos del bucle fa alguna cosa —enviar un correu, incrementar un comptador global, escriure a la pantalla—, continuar iterant després de trobar la resposta no és només lent, és incorrecte. Sense break, la cerca de la primera tasca vençuda enviaria un avís per cada tasca vençuda, i la Marta rebria cinc correus en lloc d'un.

Norma: quan la pregunta és "n'hi ha alguna?" o "quina és la primera?", surt amb break. Quan la pregunta és "quantes?" o "quin és el total?", cal recórrer-ho tot i no hi ha break que valgui.

  1. continue: saltar-se una iteració

continue salta la resta del cos i passa a la iteració següent. No surt del bucle: en un for, executa l'actualització i torna a comprovar la condició.

L'ús més habitual és descartar elements que no interessen al principi del cos, deixant la resta del codi sense indentar. És la clàusula de guarda de la lliçó 02-01, ara sí amb una sortida de debò:

let horesActives = 0;

for (let i = 0; i < ids.length; i++) {
  if (arxivades[i]) continue;            // les arxivades no compten
  if (estats[i] === 'feta') continue;    // les fetes tampoc

  horesActives += hores[i];
  console.log(`[${ids[i]}] ${titols[i]} · ${hores[i]} h`);
}

console.log(`Hores actives al tauler: ${horesActives} h`);

Sortida:

[1] Redissenyar la sala polivalent · 12 h
[2] Cartelleria del taller de serigrafia · 6 h
[3] Actualitzar el web de reserves · 14 h
[5] Guia sobre enquadernació per a residents · 8 h
[6] Pressupost de la fusteria · 5 h
Hores actives al tauler: 45 h

Compara aquesta estructura amb l'equivalent fent servir un if embolcallant:

// △ Mateix resultat, un nivell més d'indentació
for (let i = 0; i < ids.length; i++) {
  if (!arxivades[i] && estats[i] !== 'feta') {
    horesActives += hores[i];
    console.log(`[${ids[i]}] ${titols[i]} · ${hores[i]} h`);
  }
}

Amb dues exclusions totes dues versions són llegibles. Amb quatre o cinc, la condició embolcallant es converteix en una línia quilomètrica plena de negacions i &&, mentre que les guardes amb continue continuen sent una llista de motius de descart, cadascun a la seva línia i amb el seu comentari. Aquest és l'avantatge real de continue: manté el cos pla i separa els descarts de la lògica principal.

Els continue d'una sola línia són l'única excepció raonable a la norma de "claus sempre" de la lliçó 02-01: if (arxivades[i]) continue; cap en una línia, no pot créixer accidentalment i es llegeix com una frase.

  1. break i continue a while i do...while

Funcionen igual als tres bucles, amb una diferència perillosa a while: continue salta a l'inici del bucle sense executar cap actualització, perquè en un while l'actualització l'escrius tu al cos.

// ✗ BUCLE INFINIT
let i = 0;
while (i < ids.length) {
  if (arxivades[i]) continue;   // ← i no s'incrementa mai: es queda a la 3 per sempre
  console.log(titols[i]);
  i++;
}

En arribar a i = 3 (la tasca arxivada), continue salta directament a comprovar la condició sense executar l'i++, així que i continua valent 3 eternament. La versió correcta incrementa abans de la guarda:

let i = -1;
while (i < ids.length - 1) {
  i++;
  if (arxivades[i]) continue;
  console.log(titols[i]);
}

El resultat és correcte però incòmode de llegir, i per això hi ha una norma senzilla: si necessites continue, fes servir un for, on l'actualització va a la capçalera i sempre s'executa.

Hi ha un altre parany que connecta amb la lliçó anterior: un break dins d'un switch surt del switch, no del bucle.

for (let i = 0; i < ids.length; i++) {
  switch (estats[i]) {
    case 'feta':
      break;        // ← surt del switch; el bucle continua amb la tasca següent
  }
  console.log(titols[i]);   // aquesta línia s'executa també per a les fetes
}

Si el que vols és abandonar el bucle des de dins d'un switch, necessites una bandera o una etiqueta, que veuràs a la secció 9.

  1. break, continue o bandera: quin fer servir

Eina Què fa Fes-la servir quan
break Acaba el bucle Busques la primera coincidència o comproves si n'hi ha alguna
continue Salta a la iteració següent Vols descartar elements i mantenir el cos pla
Bandera booleana Recorda que ha passat alguna cosa Necessites continuar recorrent i recordar la troballa
Condició a la capçalera Atura el bucle netament La condició d'aturada és simple i natural (while (!trobada && i < n))

Aquesta última fila mereix un exemple, perquè és l'alternativa elegant a break quan el bucle és un while:

let index = 0;
let trobada = false;

while (!trobada && index < ids.length) {
  if (datesLimit[index] < AVUI && estats[index] !== 'feta') {
    trobada = true;
  } else {
    index++;
  }
}

console.log(trobada ? `Vençuda: ${titols[index]}` : 'Cap de vençuda');

La condició de la capçalera declara totes les raons per les quals el bucle pot acabar, sense que calgui buscar un break amagat vint línies més avall. Amb bucles llargs és més llegible; amb bucles curts, break guanya per simplicitat. Totes dues són correctes.

  1. Bucles imbricats

Un bucle imbricat és un bucle dins del cos d'un altre. L'intern s'executa sencer a cada iteració de l'extern.

El cas del projecte: la Marta vol el repartiment de tasques per persona sense escriure un comptador per cada membre de l'equip, com vas fer a la lliçó 02-02.

const equip = ['Marta', 'Iván', 'Lucía'];

for (let p = 0; p < equip.length; p++) {           // bucle extern: persones
  let quantes = 0;
  let horesPersona = 0;

  for (let t = 0; t < ids.length; t++) {           // bucle intern: tasques
    if (responsables[t] !== equip[p]) continue;
    if (estats[t] === 'feta') continue;

    quantes++;
    horesPersona += hores[t];
  }

  console.log(`${equip[p].padEnd(6)} ${quantes} tasca/es obertes · ${horesPersona} h`);
}

Sortida:

Marta  1 tasca/es obertes · 6 h
Iván   3 tasca/es obertes · 25 h
Lucía  1 tasca/es obertes · 14 h

El mateix informe de la lliçó 02-02, però sense tres variables repetides i sense cadena d'else if: si demà entra algú nou a l'equip, se n'afegeix el nom a l'array equip i el codi no canvia. Aquest és el benefici de la imbricació: converteix una repetició escrita a mà en una repetició generada.

Fixa't en dos detalls:

  • Els comptadors quantes i horesPersona es declaren dins del bucle extern. S'han de reiniciar a cada persona. Si fossin del tot a fora, acumularien els totals de l'equip sencer.
  • Cada bucle té la seva pròpia variable de control, p i t. Reutilitzar i a tots dos és l'error clàssic dels bucles imbricats: l'intern la deixa amb el valor final i l'extern acaba abans d'hora.
flowchart TD
    A["p = 0 · Marta"] --> B["t = 0..5<br/>recorre les 6 tasques"]
    B --> C["Imprimeix la fila de la Marta"]
    C --> D["p = 1 · Iván"]
    D --> E["t = 0..5<br/>recorre les 6 tasques"]
    E --> F["Imprimeix la fila de l'Iván"]
    F --> G["p = 2 · Lucía"]
    G --> H["t = 0..5<br/>recorre les 6 tasques"]
    H --> I["Imprimeix la fila de la Lucía"]
    I --> J["Fi: 3 × 6 = 18 iteracions internes"]

  1. El creixement del nombre d'iteracions

El bucle intern s'executa una vegada sencer per cada volta de l'extern. El total d'iteracions internes és el producte de les dues mides.

Persones Tasques Iteracions internes
3 6 18
3 400 1 200
10 400 4 000
400 400 160 000

Les tres primeres files són irrellevants per a un ordinador. L'última il·lustra el perill: quan tots dos bucles recorren la mateixa llista de dades, el cost creix amb el quadrat de la mida. Doblar les dades quadruplica la feina. Això s'anomena cost quadràtic i és la causa més freqüent que una interfície es quedi congelada; ho estudiaràs amb detall a Optimització del Rendiment.

Dues regles pràctiques per al nivell on ets:

  1. Imbricar un bucle petit (l'equip: 3) amb un de gran (les tasques: 400) és perfectament acceptable. El cost és 3 × 400, és a dir, lineal amb un factor constant.
  2. Imbricar dos bucles sobre la mateixa llista gran és el senyal d'alarma. Comparar cada tasca amb totes les altres —per detectar títols duplicats, per exemple— és exactament aquest cas. Sol existir una solució amb un sol recorregut i un objecte de consulta; ho veuràs al Mòdul 4.

Un tercer nivell d'imbricació (persones × estats × tasques) gairebé sempre indica que el problema es pot reformular. Si et trobes escrivint el tercer for, atura't a pensar.

  1. break i continue dins de bucles imbricats

Regla fonamental: break i continue afecten únicament el bucle més intern que els conté.

for (let p = 0; p < equip.length; p++) {
  for (let t = 0; t < ids.length; t++) {
    if (responsables[t] === equip[p] && datesLimit[t] < AVUI && estats[t] !== 'feta') {
      console.log(`${equip[p]} té una tasca vençuda: ${titols[t]}`);
      break;      // ← surt del bucle de TASQUES, no del de PERSONES
    }
  }
}

Sortida:

Iván té una tasca vençuda: Pressupost de la fusteria

El break talla la cerca dins de la persona actual —una tasca vençuda per persona ja n'hi ha prou per a l'avís— i el bucle extern continua amb la següent. És exactament el comportament que volem aquí, i en la gran majoria de casos reals.

El problema apareix quan vols abandonar els dos bucles alhora: per exemple, aturar-te tan bon punt trobis la primera persona amb una tasca vençuda, sense continuar revisant la resta de l'equip. Amb break a seques no es pot. La solució tradicional és una bandera comprovada als dos nivells:

let culpable = '';

for (let p = 0; p < equip.length && culpable === ''; p++) {
  for (let t = 0; t < ids.length; t++) {
    if (responsables[t] === equip[p] && datesLimit[t] < AVUI && estats[t] !== 'feta') {
      culpable = equip[p];
      break;
    }
  }
}

console.log(culpable !== '' ? `Primer avís per a ${culpable}` : 'Tot en termini');
// Primer avís per a Iván

La condició extra && culpable === '' a la capçalera del bucle extern fa la feina. Funciona, però cal recordar-se d'escriure-la, i amb tres nivells d'imbricació es torna incòmoda. Per a això existeixen les etiquetes.

  1. Etiquetes: break etiqueta i continue etiqueta

Una etiqueta (label) és un nom seguit de dos punts col·locat just abans d'un bucle. Permet que break i continue indiquin a quin bucle es refereixen.

let culpable = '';

cerca:                                             // ← l'etiqueta
for (let p = 0; p < equip.length; p++) {
  for (let t = 0; t < ids.length; t++) {
    if (responsables[t] === equip[p] && datesLimit[t] < AVUI && estats[t] !== 'feta') {
      culpable = equip[p];
      break cerca;                                 // ← surt dels DOS bucles
    }
  }
}

console.log(`Primer avís per a ${culpable}`);      // Primer avís per a Iván

break cerca; acaba el bucle etiquetat com a cerca, és a dir, l'extern, i amb ell tot el que conté. L'execució continua al console.log.

continue etiqueta és menys freqüent però també existeix: salta a la iteració següent del bucle etiquetat.

revisioPersona:
for (let p = 0; p < equip.length; p++) {
  for (let t = 0; t < ids.length; t++) {
    if (responsables[t] !== equip[p]) continue;

    if (hores[t] > 40) {
      console.error(`${equip[p]}: tasca que incompleix la R3. Es salta aquesta persona.`);
      continue revisioPersona;    // ← abandona aquesta persona, passa a la següent
    }
    console.log(`${equip[p]} · ${titols[t]}`);
  }
}

Detalls de sintaxi i ús:

  • L'etiqueta va immediatament abans del bucle, normalment a la seva pròpia línia i sense indentar.
  • El nom segueix les regles dels identificadors de la lliçó 01-04, i per convenció s'escriu en minúscules o camelCase descrivint el propòsit: cerca, revisioPersona.
  • break etiqueta i continue etiqueta només es poden fer servir dins del bucle etiquetat.
  • Tècnicament una etiqueta pot marcar qualsevol bloc, no només un bucle, però això és raríssim i no ho necessitaràs.

Per què es fan servir poc. Les etiquetes són codi correcte i legal, però molts equips les prohibeixen a la seva guia d'estil, per dos motius:

  1. Són cosines del goto, i saltar a un punt marcat des de l'interior de diversos blocs trenca la lectura seqüencial del codi.
  2. Gairebé sempre hi ha una alternativa millor, i aquesta alternativa sol ser extreure el bucle intern a una funció i sortir amb return —ho veuràs a Definició i Crida de Funcions—.

Quan són la solució neta: quan tens dos o tres bucles imbricats, la sortida primerenca afecta tots i extreure una funció complicaria el codi més del que el simplifica. En aquesta situació concreta, break etiqueta és més clar que tres banderes comprovades a tres capçaleres.

  1. Alternatives més llegibles que les etiquetes

Abans de recórrer a una etiqueta, considera aquestes tres opcions en ordre:

1. Bandera a la condició del bucle extern. És la de la secció 8. Avantatge: la condició d'aturada és visible a la capçalera. Inconvenient: cal recordar-se d'escriure-la als dos llocs.

2. Extreure la lògica a una funció i fer servir return. L'opció que faràs servir a partir del Mòdul 3. return surt de la funció sencera, travessant tots els bucles de cop, i a més dona un nom a l'operació:

// Avançament del Mòdul 3 — encara no cal que ho entenguis
function cercarPrimerCulpable(equip, responsables, datesLimit, estats, avui) {
  for (let p = 0; p < equip.length; p++) {
    for (let t = 0; t < responsables.length; t++) {
      if (responsables[t] === equip[p] && datesLimit[t] < avui && estats[t] !== 'feta') {
        return equip[p];      // surt dels dos bucles i de la funció
      }
    }
  }
  return null;
}

Compara això amb la versió etiquetada: el nom cercarPrimerCulpable documenta la intenció, el resultat es pot reutilitzar i no hi ha cap etiqueta per interpretar. Aquesta és la raó de fons per la qual les etiquetes es veuen tan poc en codi modern.

3. Reformular per no necessitar la imbricació. Moltes vegades el segon bucle hi sobra. L'exemple del culpable es pot resoldre amb un sol recorregut de les tasques, perquè cada tasca ja sap qui n'és el responsable:

let culpable = '';

for (let t = 0; t < ids.length; t++) {
  if (datesLimit[t] < AVUI && estats[t] !== 'feta') {
    culpable = responsables[t];
    break;
  }
}

console.log(`Primer avís per a ${culpable}`);   // Primer avís per a Iván

Sis iteracions en el pitjor cas en lloc de divuit, un sol break sense etiqueta i menys codi. Quan un bucle imbricat t'obliga a fer servir etiquetes, sospita primer de la imbricació. Molt sovint el problema es pot recórrer una sola vegada.

  1. Cas pràctic: la matriu responsable × estat

El tauler de Nómada Tasques necessita una taula creuada: quantes tasques té cada persona a cada estat. És l'exemple canònic de bucle imbricat, aquesta vegada creuant dues llistes de configuració —equip i ESTATS— contra la llista de dades.

const equip = ['Marta', 'Iván', 'Lucía'];
const ESTATS = ['pendent', 'en-curs', 'feta'];

console.log('Persona   pendent    en-curs   feta    TOTAL');
console.log('------------------------------------------------');

const totalsPerEstat = [0, 0, 0];

for (let p = 0; p < equip.length; p++) {
  let fila = equip[p].padEnd(10);
  let totalPersona = 0;

  for (let e = 0; e < ESTATS.length; e++) {
    let compte = 0;

    for (let t = 0; t < ids.length; t++) {
      if (arxivades[t]) continue;
      if (responsables[t] !== equip[p]) continue;
      if (estats[t] !== ESTATS[e]) continue;
      compte++;
    }

    fila += String(compte).padEnd(11);
    totalPersona += compte;
    totalsPerEstat[e] += compte;
  }

  console.log(fila + totalPersona);
}

console.log('------------------------------------------------');
console.log(
  'TOTAL     ' +
  String(totalsPerEstat[0]).padEnd(11) +
  String(totalsPerEstat[1]).padEnd(10) +
  String(totalsPerEstat[2]).padEnd(8) +
  (totalsPerEstat[0] + totalsPerEstat[1] + totalsPerEstat[2])
);

Sortida:

Persona   pendent    en-curs   feta    TOTAL
------------------------------------------------
Marta     1          0          0          1
Iván      1          2          0          3
Lucía     1          0          0          1
------------------------------------------------
TOTAL     3          2         0       5

Què està passant aquí, capa per capa:

  • Tres nivells d'imbricació: 3 persones × 3 estats × 6 tasques = 54 iteracions internes. Amb tres persones i tres estats fixos, aquests factors són constants, així que el cost real creix només amb el nombre de tasques.
  • Les tres guardes amb continue descarten el que no encaixa a la cel·la actual. Escrites com una sola condició serien !arxivades[t] && responsables[t] === equip[p] && estats[t] === ESTATS[e], correcta però molt menys llegible.
  • La tasca 4 (Inventari de tintes) no apareix en cap cel·la perquè està arxivada: la primera guarda l'exclou. Per això la columna feta és a zero i el total és 5 i no 6.
  • totalsPerEstat és un array acumulador: es declara abans de tot i s'incrementa per índex d'estat, sumant les columnes a mesura que es calculen.
  • fila es construeix per concatenació dins del bucle d'estats i s'imprimeix una sola vegada per persona. Acumular text en una variable és el mateix patró acumulador de la lliçó 02-02, amb '' com a valor neutre.

Aquest tercer nivell d'imbricació és just el que la secció 7 aconsellava evitar, i té solució: recórrer les tasques una sola vegada i anar sumant a la cel·la corresponent. Però això exigeix indexar per dues claus alhora, que és precisament el que fan els objectes del Mòdul 4. Per ara, tres bucles sobre sis tasques és una solució correcta i perfectament assumible.

Errors Habituals i Consells

Creure que break surt de tots els bucles. Surt només del més intern. Per sortir de diversos, etiqueta o bandera.

Fer servir break dins d'un switch esperant sortir del bucle. Aquest break pertany al switch. És una fallada silenciosa i molt difícil de veure.

continue en un while sense haver actualitzat el comptador. Bucle infinit garantit. Si necessites continue, fes servir for.

Reutilitzar la variable de control en bucles imbricats.

// ✗ El bucle intern destrueix l'índex de l'extern
for (let i = 0; i < equip.length; i++) {
  for (let i = 0; i < ids.length; i++) { }   // ombreja l'extern; comportament confús
}

Fes servir noms diferents i, encara millor, descriptius: p per a persones, t per a tasques, e per a estats.

Declarar l'acumulador al nivell equivocat. En un bucle imbricat, la posició de la declaració decideix el significat: fora de tot és el total global, dins de l'extern és el total per persona, dins de l'intern no acumula res. És la font número u de resultats estranys en taules creuades.

Posar codi després d'un break al mateix bloc. No s'executa mai. Alguns editors el marquen en gris; si veus codi gris, hi ha lògica morta.

Abusar de continue. Tres guardes al principi del cos són excel·lents; vuit continue repartits per cinquanta línies converteixen el bucle en un laberint. Si arribes a aquest punt, el bucle necessita partir-se en funcions.

Consell: posa el break tan a prop com puguis de la condició que el justifica. Un break al final d'un cos llarg és difícil de relacionar amb la seva causa.

Consell: comenta per què surts, no que surts. break; // ja l'hem trobada no aporta res; break; // amb un avís per persona n'hi ha prou sí.

Consell: per depurar un bucle imbricat, imprimeix els dos índexs junts. console.log(p, t, equip[p], titols[t]) com a primera línia del cos intern et mostra el recorregut complet i revela a l'instant si un comptador és al nivell equivocat.

Exercicis

Exercici 1 — Primera tasca disponible per a algú

Escriu un bucle que trobi la primera tasca pendent, no arxivada i sense vèncer que podria començar la Lucía, la càrrega actual de la qual és la més baixa de l'equip. Fes servir continue per als descarts i break per aturar-te tan bon punt la trobis. Mostra'n l'id, el títol i les hores. Si no n'hi ha cap, indica-ho.

Exercici 2 — Repartiment equilibrat

Recorre l'equip amb un bucle imbricat i calcula, per a cada persona, les seves hores obertes (estat diferent de 'feta' i no arxivada). Després determina qui té més hores i qui en té menys, i suggereix moure una tasca. Ves amb compte amb on declares cada acumulador.

Exercici 3 — Etiquetes contra reformulació

El taller no admet dues tasques obertes amb la mateixa prioritat i el mateix responsable vencent el mateix mes. Escriu un bucle imbricat que compari cada tasca amb les posteriors i s'aturi al primer conflicte, fent servir break etiqueta. Després respon: quantes comparacions fa aquest algorisme amb 6 tasques? I amb 400?

Solucions

Exercici 1

let idDisponible = 0;
let titolDisponible = '';
let horesDisponible = 0;

for (let t = 0; t < ids.length; t++) {
  if (arxivades[t]) continue;                    // fora del tauler
  if (estats[t] !== 'pendent') continue;         // ja començada o acabada
  if (datesLimit[t] < AVUI) continue;            // vençuda: la reassigna la Marta

  idDisponible = ids[t];
  titolDisponible = titols[t];
  horesDisponible = hores[t];
  break;                                         // amb la primera ja n'hi ha prou
}

if (idDisponible === 0) {
  console.log('No hi ha cap tasca disponible per assignar a la Lucía.');
} else {
  console.log(`Suggeriment per a la Lucía: [${idDisponible}] ${titolDisponible} (${horesDisponible} h)`);
}
// Suggeriment per a la Lucía: [2] Cartelleria del taller de serigrafia (6 h)

El recorregut descarta la tasca 1 (està en-curs) i es queda amb la 2, que és pendent, no està arxivada i venç el 2026-10-15, encara per davant d'AVUI. Dues iteracions de sis.

Les tres guardes estan ordenades de més barata i més excloent a menys: primer un booleà directe, després una comparació de textos, i finalment la de dates. No canvia el resultat, però és un bon hàbit: quan les comprovacions siguin costoses, l'ordre importarà.

idDisponible = 0 funciona com a sentinella perquè la regla R1 garanteix que els identificadors són enters positius: el 0 no pot ser un id real.

Exercici 2

const equip = ['Marta', 'Iván', 'Lucía'];

let maxHores = -1;
let personaMax = '';
let minHores = Infinity;
let personaMin = '';

for (let p = 0; p < equip.length; p++) {
  let horesPersona = 0;                    // ← es reinicia a cada persona

  for (let t = 0; t < ids.length; t++) {
    if (arxivades[t]) continue;
    if (estats[t] === 'feta') continue;
    if (responsables[t] !== equip[p]) continue;
    horesPersona += hores[t];
  }

  console.log(`${equip[p].padEnd(6)} ${horesPersona} h obertes`);

  if (horesPersona > maxHores) {
    maxHores = horesPersona;
    personaMax = equip[p];
  }
  if (horesPersona < minHores) {
    minHores = horesPersona;
    personaMin = equip[p];
  }
}

console.log(`\nMés carregat:  ${personaMax} (${maxHores} h)`);
console.log(`Menys carregat: ${personaMin} (${minHores} h)`);
console.log(`Desequilibri: ${maxHores - minHores} h`);

if (maxHores - minHores > 10) {
  console.warn(`La Marta hauria de moure alguna tasca de ${personaMax} a ${personaMin}.`);
}

Sortida:

Marta  6 h obertes
Iván   25 h obertes
Lucía  14 h obertes

Més carregat:  Iván (25 h)
Menys carregat: Marta (6 h)
Desequilibri: 19 h

L'important és on viu cada variable:

Variable On es declara Per què
horesPersona Dins del bucle extern Ha de començar a 0 per a cada persona
maxHores, personaMax Fora de tots dos bucles Han de sobreviure a totes les persones

Si horesPersona es declarés fora, aniria acumulant el total de l'equip i cada fila mostraria un nombre més gran que l'anterior: 6, 31, 45. És un error que produeix nombres plausibles i per això costa detectar-lo. Si maxHores es declarés dins, es reiniciaria a cada persona i el resultat final seria sempre el de l'última.

Infinity com a valor inicial de minHores és el sentinella idoni per a un mínim: qualsevol nombre real és menor, així que la primera persona sempre el substitueix. El vas conèixer a Variables i Tipus de Dades.

Exercici 3

let conflicte = false;
let tascaA = 0;
let tascaB = 0;

comparacio:
for (let a = 0; a < ids.length; a++) {
  if (estats[a] === 'feta' || arxivades[a]) continue;

  for (let b = a + 1; b < ids.length; b++) {          // ← b comença a a + 1
    if (estats[b] === 'feta' || arxivades[b]) continue;

    const mateixaPrioritat = prioritats[a] === prioritats[b];
    const mateixResponsable = responsables[a] === responsables[b];
    const mateixMes = datesLimit[a].slice(0, 7) === datesLimit[b].slice(0, 7);

    if (mateixaPrioritat && mateixResponsable && mateixMes) {
      conflicte = true;
      tascaA = ids[a];
      tascaB = ids[b];
      break comparacio;                               // ← surt dels dos bucles
    }
  }
}

if (conflicte) {
  console.error(`Conflicte de planificació entre les tasques ${tascaA} i ${tascaB}.`);
} else {
  console.log('No hi ha conflictes de planificació.');
}
// Conflicte de planificació entre les tasques 1 i 6.

datesLimit[a].slice(0, 7) es queda amb els set primers caràcters de la data ISO, és a dir, '2026-09': any i mes. És la manera més simple de comparar "mateix mes" quan les dates són text en format ISO.

El backlog sí que té un conflicte: la tasca 1 (Redissenyar la sala polivalent, Iván, prioritat alta, venç el 2026-09-30) i la tasca 6 (Pressupost de la fusteria, Iván, prioritat alta, venç el 2026-09-05) comparteixen responsable, prioritat i mes. L'algorisme ho detecta en comparar la parella a = 0, b = 5, i l'etiqueta hi talla els dos bucles.

Aquest resultat il·lustra una cosa valuosa: en aplicar una regla de negoci nova sobre dades reals solen aparèixer conflictes que ningú no havia vist. La Marta hauria de decidir si mou una de les dues tasques a l'octubre o si relaxa la regla.

El detall clau de l'algorisme és let b = a + 1. Si b comencés a 0, cada parella es compararia dues vegades (A amb B i B amb A) i, encara pitjor, cada tasca es compararia amb ella mateixa, produint sempre un conflicte fals. Començar a a + 1 compara cada parella exactament una vegada.

Nombre de comparacions: amb n tasques, el bucle fa n × (n - 1) / 2 comparacions.

Tasques Comparacions
6 15
40 780
400 79 800

Multiplicar per 10 el nombre de tasques multiplica per 100 la feina: és el cost quadràtic de la secció 7 en estat pur. Amb 400 tasques i un càlcul tan simple continua sent instantani, però si dins del bucle calgués pintar alguna cosa o demanar dades a un servidor, l'aplicació es pararia. La solució —agrupar les tasques per la clau responsable + prioritat + mes en un sol recorregut— arriba al Mòdul 4.

I observa que en aquest cas concret l'etiqueta sí que està justificada: la sortida primerenca afecta dos bucles, la bandera exigiria comprovar-la a les dues capçaleres i encara no tens funcions per extreure la cerca. És exactament el nínxol de break etiqueta.

Conclusió

Ja controles el flux dins d'un bucle amb precisió. Saps que break acaba el bucle a l'acte i que és l'eina correcta per a "la primera coincidència" i per a "n'hi ha alguna?", no només per velocitat sinó perquè continuar iterant després de trobar la resposta pot duplicar efectes secundaris. Saps que continue salta a la iteració següent i que el seu millor ús són les guardes al principi del cos, que mantenen el codi pla i separen els descarts de la lògica principal. I en coneixes els dos paranys: continue en un while sense actualitzar el comptador provoca un bucle infinit, i un break dins d'un switch surt del switch, no del bucle.

Saps imbricar bucles per creuar dues llistes —l'equip contra el backlog—, col·locar cada acumulador al nivell correcte i estimar-ne el cost: acceptable quan un dels bucles és petit i fix, perillós quan tots dos recorren la mateixa llista gran. Coneixes les etiquetes, break etiqueta i continue etiqueta, saps quan són la solució neta i per què en la majoria dels casos hi ha alguna cosa millor: una bandera a la capçalera, un sol recorregut ben plantejat o, a partir del Mòdul 3, una funció amb return.

Aplicat a Nómada Tasques, ja has construït la matriu responsable × estat del tauler, has detectat el desequilibri de càrrega entre l'Iván i la Marta i has escrit la primera regla de planificació que compara tasques entre si.

Tot això assumeix una cosa: que les dades són correctes. Però què passa quan el títol arriba buit, quan horesEstimades és el text 'dotze' o quan algú intenta llegir un camp d'una tasca que no existeix? Fins ara el teu codi hauria produït un NaN silenciós o s'hauria aturat amb un error incomprensible. A Gestió d'Errors amb try-catch, l'última lliçó del mòdul, aprendràs a detectar aquestes situacions, a llançar errors amb missatges útils i a decidir què fer quan alguna cosa va malament, sense que l'aplicació sencera s'ensorri.

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