7 votes

Est-il possible d'ajouter une méthode d'annulation à une Promise dans Typescript ?

J'aimerais créer une QueryPromise qui soit juste une promesse concrète avec une méthode d'annulation. Voici comment l'utiliser :

function runQuery(text: string): QueryPromise {
  return new QueryPromise((resolve,reject) => {nativeQuery(resolve)})
}

Voici ma première tentative, que j'espère assez compréhensible :

interface CancellablePromise<T> extends Promise<T> {
  cancel: () => void
}

// create the promise my app will use
type QueryPromise = CancellablePromise<string|boolean>

Mais ce n'est pas suffisant.

Après quelques heures d'essais et d'erreurs, j'ai réussi à m'en approcher, mais cela semble fastidieux et tout sauf DRY.

interface CancellablePromise<T> extends Promise<T> {
  cancel: () => void
}
// also need this interface so the variable can be declared
// plus need to redeclare methods to return the new type
interface CancellablePromiseConstructor extends PromiseConstructor {
  new <T>(executor: (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): CancellablePromise<T>;
  cancel: () => void
}

type QueryPromise = CancellablePromise<string|boolean>    // for parameter types
var QueryPromise: CancellablePromiseConstructor = Promise // for the new operator

// some example code to exercise the promise
function runQuery(text: string): QueryPromise {
  return new QueryPromise((resolve,reject) => {nativeQuery(resolve)})
}

J'ai l'impression d'avoir fait fausse route... Quelqu'un connaît-il une meilleure façon de procéder ?

5voto

Paleo Points 7884

TypeScript interfaces y types décrire les contrats. Les vôtres sont bien :

interface CancellablePromise<T> extends Promise<T> {
  cancel: () => void
}

type QueryPromise = CancellablePromise<string | boolean>

Vous pouvez ensuite mettre en œuvre un contrat comme vous le souhaitez. Voici un exemple :

function runQuery(text: string): QueryPromise {
  let rejectCb: (err: Error) => void
  let p: Partial<QueryPromise> = new Promise<string | boolean>((resolve, reject) => {
    rejectCb = reject
    /* ... Here the implementation of the query ... */
  });
  p.cancel = () => {
    /* ... Here the implementation of the aborting ... */
    rejectCb(new Error("Canceled"))
  }
  return p as QueryPromise
}

Avis :

  • La mise en œuvre de cancel devrait rejeter la promesse ;
  • J'utilise Partial<QueryPromise> afin d'ajouter le membre cancel après.

1voto

kayahr Points 4326

J'ai mis en place quelque chose comme ça il y a quelques mois. Ma solution ne correspond pas exactement à l'API que vous souhaitez, mais elle peut peut-être fonctionner pour vous ou vous pouvez l'adapter à vos besoins :

En Annulé erreur

Tout d'abord, vous devez savoir que c'est une mauvaise idée d'avoir une promesse qui n'est pas résolue et qui n'est pas du tout rejetée. Un troisième état (annulé) va casser le code existant et une application qui n'est pas modifiée pour rechercher explicitement l'annulation va se bloquer pour toujours. C'est pourquoi je rejette une promesse avec un bouton spécial Canceled erreur lorsque la promesse est annulée. Un gestionnaire de rejet conscient de l'annulation peut distinguer un échec de l'annulation en regardant le type d'erreur (avec instanceof ). Un gestionnaire de rejet ne tenant pas compte de l'annulation traitera l'annulation comme une erreur.

Voici une implémentation simple d'une telle classe d'erreur personnalisée :

class Canceled extends Error {
    constructor(reason: string = "") {
        super(reason);
        Object.setPrototypeOf(this, Canceled.prototype);
    }
}

En setPrototypeOf est nécessaire pour que instanceof peut être utilisé pour détecter une instance de ce type d'erreur.

En Annulable interface

Ensuite, vous avez besoin d'une interface pour la promesse annulable. Le mien est à peu près comme le vôtre :

interface Cancelable<T> extends Promise<T> {
    cancel(reason?: string): Cancelable<T>;
}

En annulable fonction

La création d'une classe d'implémentation de l'interface Cancelable est une mauvaise idée. Il est difficile d'hériter du type Promise intégré. J'ai donc décidé d'utiliser un objet Promise standard et d'ajouter simplement l'attribut cancel à l'instance Promise au lieu de créer un nouveau type. Pour cela, j'utilise cette fonction :

function cancelable<T>(promise: Promise<T>, onCancel?: (canceled: Canceled) => void): Cancelable<T> {
    let cancel: ((reason: string) => Cancelable<T>) | null = null;
    let cancelable: Cancelable<T>;
    cancelable = <Cancelable<T>>new Promise((resolve, reject) => {
        cancel = (reason: string = "") => {
            try {
                if (onCancel) {
                    onCancel(new Canceled(reason));
                }
            } catch (e) {
                reject(e);
            }
            return cancelable;
        };
        promise.then(resolve, reject);
    });
    if (cancel) {
        cancelable.cancel = cancel;
    }
    return cancelable;
}

Cette fonction prend donc une Promesse normale comme premier paramètre et une onCancel en tant que second paramètre, qui est appelé lorsque la fonction cancel() est appelée sur l'objet retourné Cancelable .

Création d'une promesse annulable

Pour créer réellement une promesse annulable, vous devez envelopper une promesse standard et votre gestionnaire d'annulation avec la fonction cancelable fonction. Voici un exemple de fonction de sommeil qui est résolue après le temps donné (Utilisation de setTimeout ) et qui peut être annulé (ce qui efface le délai d'attente avec clearTimeout ):

function sleep(ms: number): Cancelable<void> {
    let timer: any;
    return cancelable(new Promise(resolve => {
        timer = setTimeout(resolve, ms);
    }), canceled => {
        clearTimeout(timer);
        throw canceled;
    });
}

Notez que le onCancel reçoit un canceled qui est une instance du Canceled type d'erreur. Le site onCancel Le callback doit lancer cette erreur lorsque la promesse a été annulée avec succès. Si la promesse ne peut pas être annulée pour une raison quelconque, il suffit de ne pas lancer l'erreur et la promesse continuera son travail habituel.

Comment annuler la promesse

Et enfin, voici un exemple de comment annuler une promesse annulable et comment y réagir :

const waiting = sleep(1000);
waiting.then(() => {
    console.log("Wait successful");
}, error => {
    if (error instanceof Canceled) {
        console.log("Wait canceled. Reason: " + error.message);
    } else {
        console.error("Wait failed:", error);
    }
});

waiting.cancel("Nah, stop waiting");

J'espère que cela fonctionnera pour vous ou au moins vous donnera quelques idées pour réaliser votre propre mise en œuvre.

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