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.