Vine aquí bastante cómodo con los dos conceptos, pero con algo que no me queda claro sobre ellos.
Después de leer algunas de las respuestas, creo que tengo una metáfora correcta y útil para describir la diferencia.
Si piensa en sus líneas de código individuales como cartas de juego separadas pero ordenadas (deténgame si le estoy explicando cómo funcionan las tarjetas perforadas de la vieja escuela), entonces, para cada procedimiento separado escrito, tendrá una pila única de cartas (no ¡copiar y pegar!) y la diferencia entre lo que normalmente sucede cuando se ejecuta el código normalmente y de forma asincrónica depende de si te importa o no.
Cuando ejecuta el código, le entrega al sistema operativo un conjunto de operaciones individuales (en las que su compilador o intérprete rompió su código de nivel "superior") para que se lo pase al procesador. Con un procesador, solo se puede ejecutar una línea de código a la vez. Entonces, para lograr la ilusión de ejecutar múltiples procesos al mismo tiempo, el sistema operativo utiliza una técnica en la que envía al procesador solo unas pocas líneas de un proceso dado a la vez, alternando entre todos los procesos de acuerdo con cómo ve ajuste. El resultado son múltiples procesos que muestran el progreso para el usuario final en lo que parece ser al mismo tiempo.
Para nuestra metáfora, la relación es que el sistema operativo siempre baraja las tarjetas antes de enviarlas al procesador. Si su pila de cartas no depende de otra pila, no se da cuenta de que dejó de ser seleccionada mientras otra pila se activaba. Entonces, si no te importa, no importa.
Sin embargo, si le importa (p. Ej., Hay múltiples procesos, o pilas de tarjetas, que dependen unos de otros), entonces la combinación aleatoria del sistema operativo arruinará sus resultados.
Escribir código asincrónico requiere manejar las dependencias entre el orden de ejecución, independientemente de cuál sea el orden. Esta es la razón por la cual se utilizan construcciones como "devoluciones de llamada". Le dicen al procesador, "lo siguiente que debe hacer es decirle a la otra pila lo que hicimos". Al usar tales herramientas, puede estar seguro de que la otra pila recibe una notificación antes de permitir que el sistema operativo ejecute más de sus instrucciones. ("If called_back == false: send (no_operation)" - no estoy seguro de si así es como se implementa, pero lógicamente, creo que es coherente).
Para procesos paralelos, la diferencia es que tiene dos pilas que no se preocupan entre sí y dos trabajadores para procesarlas. Al final del día, es posible que deba combinar los resultados de las dos pilas, lo que sería una cuestión de sincronía, pero, para la ejecución, no volverá a importarle.
No estoy seguro si esto ayuda, pero siempre encuentro útiles las explicaciones múltiples. Además, tenga en cuenta que la ejecución asincrónica no está limitada a una computadora individual y sus procesadores. En términos generales, se trata del tiempo, o (incluso más en general) un orden de eventos. Entonces, si envía la pila dependiente A al nodo de red X y su pila acoplada B a Y, el código asincrónico correcto debería ser capaz de explicar la situación como si se estuviera ejecutando localmente en su computadora portátil.