Le problème que vous décrivez est double.
- Le programme que vous écrivez doit se comporter de manière asynchrone dans son ensemble lorsqu'il est vu de l'extérieur .
- Il ne devrait pas être visible sur le site de l'appel, qu'un appel de fonction abandonne potentiellement le contrôle ou non.
Il y a deux façons d'y parvenir, mais elles se résument essentiellement à
- avoir plusieurs threads (à un certain niveau d'abstraction)
- ayant plusieurs types de fonctions au niveau du langage, qui sont tous appelés comme ça
foo(4, 7, bar, quux)
.
Pour (1), je suis en train de regrouper forking et exécuter plusieurs processus, engendrant plusieurs threads du noyau et des implémentations de threads verts qui planifient les threads de niveau d'exécution du langage sur les threads du noyau. Du point de vue du problème, ce sont les mêmes. Dans ce monde, aucune fonction n'abandonne ou ne perd le contrôle du point de vue de son fil . Le thread lui-même n'a parfois pas de contrôle et parfois ne fonctionne pas, mais vous n'abandonnez pas le contrôle de votre propre thread dans ce monde. Un système correspondant à ce modèle peut ou non avoir la capacité de générer de nouveaux threads ou de se joindre à des threads existants. Un système correspondant à ce modèle peut ou non avoir la capacité de dupliquer un thread comme Unix fork
.
(2) est intéressant. Pour que justice soit faite, nous devons parler de formulaires d'introduction et d'élimination.
Je vais montrer pourquoi l'implicite await
ne peut pas être ajouté à une langue comme Javascript d'une manière rétrocompatible. L'idée de base est qu'en exposant les promesses à l'utilisateur et en distinguant les contextes synchrone et asynchrone, Javascript a divulgué un détail d'implémentation qui empêche la gestion uniforme des fonctions synchrones et asynchrones. Il y a aussi le fait que vous ne pouvez pas faire de await
promesse en dehors d'un corps de fonction asynchrone. Ces choix de conception sont incompatibles avec "rendre l'asynchronisme invisible pour l'appelant".
Vous pouvez introduire une fonction synchrone à l'aide d'un lambda et l'éliminer avec un appel de fonction.
Introduction de la fonction synchrone:
((x) => {return x + x;})
Élimination de la fonction synchrone:
f(4)
((x) => {return x + x;})(4)
Vous pouvez comparer cela avec l'introduction et l'élimination de la fonction asynchrone.
Introduction à la fonction asynchrone
(async (x) => {return x + x;})
Élimination de la fonction asynchrone (remarque: uniquement valable à l'intérieur d'une async
fonction)
await (async (x) => {return x + x;})(4)
Le problème fondamental ici est qu'une fonction asynchrone est également une fonction synchrone produisant un objet de promesse .
Voici un exemple d'appel synchrone d'une fonction asynchrone dans le repl node.js.
> (async (x) => {return x + x;})(4)
Promise { 8 }
Vous pouvez hypothétiquement avoir un langage, même typé dynamiquement, où la différence entre les appels de fonction asynchrones et synchrones n'est pas visible sur le site d'appel et n'est peut-être pas visible sur le site de définition.
Prendre un langage comme celui-ci et le réduire à Javascript est possible, il vous suffirait de rendre efficacement toutes les fonctions asynchrones.