Sono venuto qui abbastanza a mio agio con i due concetti, ma con qualcosa che non mi è chiaro su di essi.
Dopo aver letto alcune delle risposte, penso di avere una metafora corretta e utile per descrivere la differenza.
Se pensi alle tue singole linee di codice come a carte separate ma ordinate (fermami se sto spiegando come funzionano le carte perforate della vecchia scuola), allora per ogni procedura separata scritta, avrai una pila unica di carte (non copia e incolla!) e la differenza tra ciò che accade normalmente quando si esegue il codice normalmente e in modo asincrono dipende dal fatto che ti interessi o meno.
Quando si esegue il codice, si passa al sistema operativo una serie di singole operazioni (in cui il compilatore o l'interprete ha interrotto il codice di livello "superiore") da passare al processore. Con un processore, è possibile eseguire solo una riga di codice alla volta. Quindi, al fine di realizzare l'illusione di eseguire più processi contemporaneamente, il sistema operativo utilizza una tecnica in cui invia al processore solo poche righe da un determinato processo alla volta, passando da un processo all'altro in base a come vede in forma. Il risultato sono molteplici processi che mostrano progressi per l'utente finale in quello che sembra essere allo stesso tempo.
Per la nostra metafora, la relazione è che il sistema operativo mescola sempre le carte prima di inviarle al processore. Se la tua pila di carte non dipende da un'altra pila, non ti accorgi che la pila ha smesso di essere selezionata mentre un'altra pila è diventata attiva. Quindi se non ti interessa, non importa.
Tuttavia, se ti interessa (ad esempio, ci sono più processi - o pile di carte - che dipendono l'uno dall'altro), la mescolanza del sistema operativo rovinerà i tuoi risultati.
La scrittura di codice asincrono richiede la gestione delle dipendenze tra l'ordine di esecuzione indipendentemente da ciò che l'ordinamento finisce per essere. Questo è il motivo per cui vengono utilizzati costrutti come "callback". Dicono al processore, "la prossima cosa da fare è dire all'altro stack cosa abbiamo fatto". Utilizzando tali strumenti, si può essere certi che l'altro stack viene avvisato prima che consenta al sistema operativo di eseguire altre istruzioni. ("Se called_back == false: send (no_operation)" - non sono sicuro che questo sia effettivamente il modo in cui viene implementato, ma logicamente, penso che sia coerente.)
Per i processi paralleli, la differenza è che hai due stack che non si preoccupano l'uno dell'altro e due lavoratori per elaborarli. Alla fine della giornata, potresti dover combinare i risultati delle due pile, che sarebbero quindi una questione di sincronicità ma, per l'esecuzione, non ti importa più.
Non sono sicuro se questo aiuta, ma trovo sempre utili più spiegazioni. Inoltre, si noti che l'esecuzione asincrona non è vincolata a un singolo computer e ai suoi processori. In generale, si occupa del tempo o (anche più in generale) di un ordine di eventi. Pertanto, se si invia lo stack dipendente A al nodo di rete X e il relativo stack accoppiato da B a Y, il codice asincrono corretto dovrebbe essere in grado di tenere conto della situazione come se fosse in esecuzione localmente sul laptop.