Controllo del flusso asincrono
Controllo del flusso asincrono
Al suo nucleo, JavaScript è progettato per essere non bloccante sul “thread” principale, che è dove vengono renderizzate le viste. Puoi immaginare l’importanza di questo nel browser. Quando il thread principale diventa bloccato, si verifica la famigerata “congelazione” che gli utenti finali temono, e nessun altro evento può essere inviato, comportando la perdita di acquisizione dati, ad esempio.
Ciò crea alcune vincoli unici che solo uno stile di programmazione funzionale può curare. Questo è dove entrano in gioco i callback.
Tuttavia, i callback possono diventare difficili da gestire in procedure più complesse. Questo porta spesso a un “inferno dei callback” in cui molte funzioni nidificate con callback rendono il codice più difficile da leggere, debuggare, organizzare, ecc.
Naturalmente, nella vita reale ci sarebbero molto probabilmente ulteriori linee di codice per gestire result1
, result2
, ecc., quindi la lunghezza e la complessità di questo problema di solito si traducono in un codice che appare molto più disordinato rispetto all’esempio sopra.
Ed è qui che le funzioni entrano in gioco. Operazioni più complesse sono composte da molte funzioni:
- stile iniziatore / input
- middleware
- terminatore
Lo “stile iniziatore / input” è la prima funzione nella sequenza. Questa funzione accetterà l’input originale, se presente, per l’operazione. L’operazione è una serie eseguibile di funzioni, e l’input originale sarà principalmente:
- variabili in un ambiente globale
- invocazione diretta con o senza argomenti
- valori ottenuti da richieste di sistema di file o di rete
Le richieste di rete possono essere richieste in arrivo avviate da una rete esterna, da un’altra applicazione sulla stessa rete o dall’app stessa sulla stessa o su una rete esterna.
Una funzione middleware restituirà un’altra funzione, e una funzione terminale invocherà il callback. Di seguito viene illustrato il flusso delle richieste di rete o di sistema di file. Qui la latenza è 0 perché tutti questi valori sono disponibili in memoria.
Gestione dello stato
Le funzioni possono essere o meno dipendenti dallo stato. La dipendenza dallo stato sorge quando l’input o un’altra variabile di una funzione dipende da una funzione esterna.
In questo modo ci sono due strategie principali per la gestione dello stato:
- passaggio diretto di variabili a una funzione e
- acquisizione di un valore di variabile da una cache, sessione, file, database, rete o altra fonte esterna.
Nota, non ho menzionato la variabile globale. Gestire lo stato con variabili globali è spesso un anti-pattern disordinato che rende difficile o impossibile garantire lo stato. Le variabili globali nei programmi complessi dovrebbero essere evitate quando possibile.
Controllo del flusso
Se un oggetto è disponibile in memoria, è possibile l’iterazione e non ci sarà alcun cambio nel flusso di controllo:
Tuttavia, se i dati esistono al di fuori della memoria, l’iterazione non funzionerà più:
Perché è successo questo? setTimeout
istruisce la CPU a memorizzare le istruzioni altrove sul bus e a pianificare il ritiro dei dati in un momento successivo. Passano migliaia di cicli della CPU prima che la funzione colpisca di nuovo al marcatore di 0 millisecondi, la CPU preleva le istruzioni dal bus e le esegue. L’unico problema è che la canzone (”) è stata restituita migliaia di cicli prima.
La stessa situazione si presenta nel trattare con i sistemi di file e le richieste di rete. Il thread principale semplicemente non può essere bloccato per un periodo di tempo indeterminato, quindi utilizziamo i callback per pianificare l’esecuzione del codice nel tempo in modo controllato.
Sarai in grado di eseguire quasi tutte le tue operazioni con i seguenti 3 pattern:
- In serie: le funzioni verranno eseguite in un rigoroso ordine sequenziale, questo è più simile ai loop
for
.
- Completamente parallelo: quando l’ordine non è un problema, come inviare e-mail a una lista di 1.000.000 di destinatari.
- Parallelo limitato: parallelo con limite, come inviare con successo 1.000.000 di destinatari da una lista di 10^7 utenti.
Ognuno ha i suoi casi d’uso, vantaggi e problemi che puoi sperimentare e approfondire ulteriormente. Ricorda soprattutto di modularizzare le tue operazioni e utilizzare i callback! Se hai dubbi, tratta tutto come se fosse middleware!