5 votes

En TypeScript, pourquoi une fonction asynchrone ne peut-elle pas retourner une union de type T | Promise<T> ?

Je suis en train de créer une API en écriture standard et certaines actions du contrôleur peuvent être synchrones alors que d'autres ne le peuvent pas. J'aimerais spécifier un type de réponse comme suit :

type ActionResult =IHttpActionResult | Promise<IHttpActionResult>;

Ensuite, au fur et à mesure que je construis des actions, lorsqu'elles deviennent basées sur des promesses, je peux simplement ajouter async et en finir.

Mais, Typescript se plaint que "le type de retour d'une fonction ou d'une méthode asynchrone doit être le type Promise global".

Pourquoi une fonction asynchrone ne peut-elle pas retourner une union de T | Promise<T> ?

Voici un exemple :

type StringPromise = Promise<string>;

// These two work as you'd expect
async function getHello(): Promise<string> {
    return 'hello';
}

async function getGoodbye(): StringPromise {
    return 'goodbye';
}

type StringyThingy = string | Promise<string>;

// the next two work as you would expect them to
function getHoorah(): StringyThingy {
    return 'hoorah!';
}

function getWahoo(): StringyThingy {
  return new Promise(resolve => resolve('wahoo'));
}

// This one results in the error:
// "the return type of an async function or method must be the global Promise type."
async function getSadface(): StringyThingy {
  return ':(';
}    

Voici quelques exemples de sorties pour le code ci-dessus :

getHello().then(console.log);
getGoodbye().then(console.log);
console.log(getHoorah());

// The library I'm using is probably using typeguards for this
// I'd imagine
const wahoo = getWahoo();
if (typeof(wahoo) === 'string') {
  console.log(wahoo);
} else {
  wahoo.then(console.log);
}

5voto

k0pernikus Points 2446

El async est un sucre syntaxique :

Cette fonction retournera toujours une promesse .

Même si vous le déclarez comme tel :

const foo = async() => 3;

C'est fondamentalement la même chose (bien que plus stricte) que :

const foo = () => new Promise(resolve => resolve(3));

ou comme :

const foo = () => Promise.resolve(3);

Tous ces exemples renverront une Promise.

La principale différence réside dans le fait que les fonctions "normales" peuvent renvoyer à la fois une Promise et d'autres types, mais qu'une fois que la Promise a été renvoyée, elle ne l'est plus. async est utilisé il, il siempre retournera une promesse.

Même si une promesse se résout immédiatement, il y a pas du tout pour un async fonction pour NE PAS retourner une promesse par la conception.

Vous devrez l'attendre / l'utiliser alors sur elle.

Ceci est également indiqué sur La référence JavaScript de Mozilla sur le mot-clé async :

La déclaration de la fonction async définit une fonction asynchrone, qui renvoie un objet AsyncFunction. Une fonction asynchrone est une fonction qui opère de manière asynchrone via la boucle d'événement, en utilisant un objet Promise implicite pour retourner son résultat. Mais la syntaxe et la structure de votre code utilisant des fonctions asynchrones ressemble beaucoup plus à l'utilisation de fonctions fonctions synchrones standard.

Et plus particulièrement sur le type de retour :

Une Promise qui sera résolue avec la valeur renvoyée par l'asynchronisme. ou rejetée avec une exception non attrapée lancée à partir de la fonction la fonction asynchrone.


En gardant cela à l'esprit, je recommande de faire de votre API async par défaut. Le monde extérieur n'a aucune importance si certaines de vos actions sont synchrones. Dans ce cas, vous pouvez résoudre la promesse immédiatement. Il n'est pas nécessaire que votre type StringyThingy = string | Promise<string>;

Type contre Promise<string> et laisser l'async gérer l'enveloppement dans la promesse pour vous ou retourner d'autres promesses dans le cas d'une utilisation async réelle. De cette façon, vous n'avez pas à vérifier l'existence d'une instance de promesse, mais vous gérez la branche async / sync de la même manière.


Si vous voulez vraiment le type union (je ne le recommande vraiment pas), alors vous devez renoncer à l'utilisation de la balise async mot-clé.

Vous pouvez définir des fonctions normales qui renvoient l'un ou l'autre type :

const foo = (x:number): Promise<number>|number => {
    if(x >=0) {
         return new Promise(resolve => resolve(x));
    } else {
         return x;
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X