Quando si ha a che fare con il sincronismo, un buon modo di pensare riguarda lo stato . Stato è tutto ciò che deve essere conservato in alcuni archivi di dati (che possono essere RAM, dischi, ... qualunque cosa, e non necessariamente sul server) in modo che l'elaborazione possa in definitiva procedere e avere successo.
Quando si fanno chiamate sincrone (genericamente, non specificamente con Node.js), il chiamante è un thread di esecuzione su qualche macchina, e lo "stato" è ciò che "thread" dell'esecuzione sa: posizione corrente nel codice, le sue variabili locali, il suo stack di chiamate. Il chiamante mantiene quello stato nella RAM per la durata della chiamata; questa è la definizione di una chiamata sincrona: mentre la chiamata non è terminata, il chiamante è bloccato. I dettagli possono variare a seconda del linguaggio di programmazione e di come viene interpretato / compilato, ma per la maggior parte delle lingue, ogni thread di esecuzione (un concetto di linguaggio) viene mappato su un thread di sistema (un concetto di sistema operativo), che significa che lo stato include una voce nella tabella di sistema dei thread, un po 'di spazio nelle strutture dello scheduler e lo stack di thread .
Ogni thread di sistema ha il proprio stack, assegnato quando il thread è stato creato. Le dimensioni predefinite per questo stack dipendono dal sistema operativo e potrebbero essere regolate a livello di programmazione. Su Linux, una pila di thread ha dimensioni 1 MB per impostazione predefinita. Questo è 1 MB di spazio indirizzo , non di RAM: le pagine effettive vengono allocate quando vi si accede, quindi un thread tipico utilizzerà solo alcune dozzine di kilobyte di "vera RAM". Anche se i dettagli variano molto a seconda del contesto, la linea di fondo è la seguente: in un'applicazione tipica, normalmente puoi gestire qualche centinaio di thread alla volta - il che significa che il tuo server può ospitare alcune centinaia in corso chiamate sincrone contemporaneamente . Oltre a questo, devi pensare.
Chiamate asincrone rimuovono questa gestione dello stato basata su thread: il chiamante riprende immediatamente il controllo e può quindi fare qualcos'altro o addirittura uscire. Tuttavia, devi comunque mantenere qualche stato da qualche parte, se non altro per ricordare che c'è una chiamata in corso e sapere cosa si dovrebbe fare con il risultato. Il punto di partenza asincrona consiste nel fare una gestione manuale e dettagliata di questo stato, che può essere piccolo come un ID di sessione di 16 byte o uno stato core simile, consentendo così molte più chiamate simultanee. A seconda del contesto, potresti persino essere in grado di scaricare lo stato sul client (il che implica, ovviamente, problemi di sicurezza aggiuntivi da affrontare). Tuttavia, l'implementazione diventa molto più complessa quando si procede in modo asincrono, e la complessità di solito viene fornita con lavoro extra, bug extra e quindi vulnerabilità extra.
Quindi andare asincrono è un ottimizzazione , e quindi dovrebbe essere previsto solo se la situazione rende la complessità e lo sforzo extra. Come per tutti gli aspetti relativi alle prestazioni, tali ottimizzazioni dovrebbero essere posticipate fino a quando un serio problema correlato alla prestazione è stato debitamente rilevato, monitorato e misurato. "L'ottimizzazione prematura è la radice di tutti i mali."
Quindi ti suggerisco di scrivere per primo tutto il tuo codice in modo sincrono. Quindi, e solo allora, i test di prestazione realistici diranno se le chiamate sincrone saranno sufficienti o meno; e se andare in asincrono è davvero giustificato, a quel punto saprai molto di più sulla tua applicazione, rendendoti pronto (o almeno più pronto) ad affrontare la complessità intrinseca della programmazione asincrona.