Model de concurrència i bucle d'events

This article needs a technical review. How you can help.

This article needs an editorial review. How you can help.

JavaScript disposa d'un model de concurrencia basat en un "bucle d'events". Aquest model és força diferent del model de concurrencia que ofereixen altres llenguatges com C o Java.

Conceptes sobre l'execució de codi

A les següents seccions s'explica el model teòric de concurrencia. Els motors de JavaScript moderns suporten concurrencia basant-se en aquest model tot i que hi implementen un gran nombre d'optimitzacions.

Representació visual

Stack, heap, queue

Stack (pila d'execució)

Les crides a funcions formen una pila de frames.

function f(b){
  var a = 12;
  return a+b+35;
}

function g(x){
  var m = 4;
  return f(m*x);
}

g(21);

Al cridar a g es crea un primer frame, que conté tots els arguments passats a la crida a més de les variables locals. Quan g crida f es crea un segon frame, que s'aplia a sobre del primer. Quan la funció f retorna el seu frame es treu de la pila (deixant només el frame de g). Quan g retorna la pila d'execució queda buida.

Heap (regió de memòria)

Els objectes s'emmagatzemen a la heap, que és una regió de memòria sense cap estructura fixa a la que el motor hi té accès.

Queue (cua d'execució)

El procès d'execució de JavaScript conté una cua de missatges, que està formada pel llistat de missatges pendents de ser processats. Cada cop que es buida s'agafa un missatge de la cua i es processa. El processament del missatge consisteix a cridar la funció associada a ell (i en conseqüència s'afegeix un frame inicial a la pila d'execució). El processament conclou quan a la pila d'execució no hi queda cap frame.

Bucle d'events

El bucle d'events rep el seu nom degut a la forma en la que normalment s'implementa, la qual sol ésser semblant al següent exemple:

while(cua.esperarMissatge()){
  cua.processarSegüentMissatge();
}

cua.esperarMissatge espera de forma síncrona que un missatge arribi si la pila és buida.

Execució ininterrompuda

Cada missatge es processa de forma complerta i ininterrompuda abans un altre missatge no és processat. Aquest fet proporciona certes propietats força útils a tenir en compte a l'hora de pensar com realitzar un programa, com pot ser el fet que mentre s'està executant una funció es garanteix que aquesta mai serà interrompuda de forma que no cal preocupar-se per que cap altre codi pugui modificar dades que puguin afectar el resultat. Aquest comportament és totalment diferent del de llenguatges com C, per exemple, on les funcions es processen en fils d'execució, que poden ser interromputs en qualsevol moment per donar pas a l'execució d'altres fils, els quals poden potencialment modificar dades que poden alterar el resultat del primer fil.

Un dels inconvenients del model emprat per JavaScript és que si un missatge triga massa a executar-se completament l'aplicació web no pot processar altres interaccions de l'usuari (que es transformen en missatges i van a parar a la cua) com ara clics o desplaçaments. Els navegadors sovint alleugen el problema tot advertint l'usuari amb el diàleg "un script està trigant massa a executar-se". Una bona pràctica consisteix a intentar que els missatges trigin poc temps a executar-se i, si és possible, trencar-los en diferents missatges més petits.

Afegir missatges

Als navegadors web els missatges s'afegeixen a la cua sempre que un event succeeix i aquest event té un event listener associat. Si no hi ha cap listener associat l'event es perd. Així per exemple si es fa un clic a un element que té associada una funció a l'event de clic afegirà un missatge a la cua. Tanmateix amb qualsevol altre tipus d'event.

Cridar a setTimeout afegirà un missatge a la cua després que el temps indicat al segon argument hagi passat. Si no hi ha cap altre missatge a la cua, aquest missage s'executarà de forma immediata. Si hi ha altres missatges, però, el missatge afegit per setTimeout esperarà a que s'hagin processat tots els altres. És per aquest motiu que el segon argument indica el temps mínim d'espera i no el temps exacte garantit fins que es processi el missatge.

Comunicació entre diferents fils d'execució

Els web worker (així s'anomenen els fils d'execució que s'encarreguen de processar missatges) i els iframe de tipus cross-origin tenen la seva pròpia pila, heap i cua de missatges. Dos fils d'execució diferents només es poden comunicar enviant-se missatges via el mètode postMessage. Aquest mètode afegirà un missatge a un altre fil d'execució sempre i quan aquest fil d'execució estigui escoltant events de tipus message (és a dir, sempre que al fil d'execució que vol rebre el missatge hi hagi associat un listener a l'event message).

Completament asíncron

Una propietat molt interessant del model de bucle d'events és que JavaScript, a diferència d'altres llenguatges, no es bloqueja mai a l'espera de res. Les operacions d'Entrada/Sortida (habitualment bloquejants a la majoria de llenguatges) són processades per JavaScript mitjançant events i callbacks, així , per exemple, mentre l'aplicació està esperant que una query a l'IndexedDB retorni o que una petició XHR retorni JavaScript pot executar altres events, com interaccions amb l'usuari, etcètera, ja que els resultats seràn rebuts i processats mitjançant un missatge diferent, que serà afegit a la cua quan els resultats estiguin llestos.

Hi ha excepcions (degudes principalment a raons històriques), com el dialog alert o bé crides síncrones de tipus XHR, però es considera una bona pràctica evitar el seu ús. Tingueu en compte que també existeixen excepcions a l'excepció, però normalment són degudes a errors d'implementació (bugs).

Document Tags and Contributors

 Contributors to this page: enTropy
 Last updated by: enTropy,