Concludiamo questo capitolo sulla struttura di un’applicazione Instant Developer Cloud descrivendo il modello di multitasking previsto per i vari tipi di container.
Come previsto dall’architettura delle virtual machine JavaScript, sia i container basati su Node.js, sia quelli basati su browser o webview (Cordova) utilizzano il meccanismo delle closure e delle callback per la gestione delle operazioni asincrone e per il multitasking cooperativo.
Questo significa che ogni operazione asincrona che coinvolge, ad esempio, query su database, lettura di dati dal file system o chiamate remote deve essere aspettata e gestita in un ramo di codice separato tramite una callback JavaScript.
Per semplificare la scrittura del codice, in particolare quello in cui si hanno diverse operazioni asincrone in cascata, Instant Developer Cloud include fino dal 2015 la sequenzializzazione automatica delle operazioni asincrone tramite il costrutto yield.
Ad esempio, se volessimo stampare nella console i numeri da 0 a 9 ad un intervallo di un secondo l’uno dall’altro, potremmo scrivere le seguenti righe di codice:
for (let i = 0; i < 10; i++) {
console.log(i);
yield app.sleep(1000);
}
Il metodo app.sleep del framework sospende il thread di codice attivo per un determinato intervallo di tempo e poi lo riprende. L’inserimento automatico della parola chiave yield consente di aspettare il termine dell’operazione asincrona prima di riprendere l’esecuzione dello script attivo.
Il risultato di questa sequenzializzazione è logicamente identico all’odierno costrutto await, con le seguenti differenze notevoli:
- L’IDE rileva automaticamente se una riga di codice chiama un metodo marcato come asincrono. In tal caso prima della chiamata viene automaticamente inserito il costrutto yield e il metodo corrente viene marcato come asincrono.
- Quando un metodo viene marcato come sincrono o asincrono (in funzione del fatto che al suo interno vengano sequenzializzate chiamate a metodi o meno), tutti i metodi che chiamano quello in fase di modifica vengono automaticamente modificati aggiungendo o togliendo il costrutto yield ove necessario.
- È possibile scegliere di non sequenzializzare una chiamata eliminando la parola chiave yield dopo che l’IDE l’ha inserita. In tal caso verrà aggiunta una callback per la gestione dei risultati della chiamata.
- Il codice dei metodi asincroni e le relative chiamate vengono generati in modo tale da gestire le chiamate asincrone. Non è necessario alcun intervento di modifica manuale.
- La gestione delle eccezioni relative ad una chiamata asincrona è identica a quella di un metodo sincrono. Non è necessaria alcuna modifica al codice applicativo.
Si deve tenere presente che durante l’attesa del completamento di un metodo asincrono il sistema non è bloccato, quindi è possibile che avvengano azioni che possono avviare ulteriori processi di codice. Se, ad esempio, lo script di gestione del clic di un pulsante richiede 10 secondi, è possibile che l’utente clicchi più volte il pulsante prima del termine del processo, causando l’avvio di più istanze dello stesso processo di gestione. In questi casi si consiglia di disabilitare il comando fino al completamento del processo, oppure di applicare un elemento di attesa all’interfaccia dell’applicazione bloccando tutti gli input dell’utente.
Notiamo infine che, nel caso di container per applicazioni web, nello stesso processo worker Node.js saranno gestite più sessioni, solitamente una quantità da 10 a 100. Se una di queste sessioni esegue un metodo sincrono molto impegnativo, tutte le sessioni dello stesso processo worker verranno rallentate. Non è mai consigliabile che uno script sincrono richieda più di 100 ms per essere eseguito. In questi casi è possibile isolare gli script sincroni lenti in un processo separato ad esempio utilizzando una sessione di tipo server, oppure suddividere il lavoro in più step sincroni separati da chiamate asincrone che permettono alle altre sessioni di continuare il proprio lavoro.