Existe-t-il une méthode pour effacer les .then
s d'une Promise
instance JavaScript ?
J'ai écrit un cadre de test JavaScript au-dessus de QUnit . Le framework exécute les tests de manière synchrone en exécutant chacun dans un fichier Promise
. (Désolé pour la longueur de ce bloc de code. Je l'ai commenté du mieux que je peux, donc cela semble moins fastidieux.)
/* Promise extension -- used for easily making an async step with a
timeout without the Promise knowing anything about the function
it's waiting on */
$$.extend(Promise, {
asyncTimeout: function (timeToLive, errorMessage) {
var error = new Error(errorMessage || "Operation timed out.");
var res, // resolve()
rej, // reject()
t, // timeout instance
rst, // reset timeout function
p, // the promise instance
at; // the returned asyncTimeout instance
function createTimeout(reject, tempTtl) {
return setTimeout(function () {
// triggers a timeout event on the asyncTimeout object so that,
// if we want, we can do stuff outside of a .catch() block
// (may not be needed?)
$$(at).trigger("timeout");
reject(error);
}, tempTtl || timeToLive);
}
p = new Promise(function (resolve, reject) {
if (timeToLive != -1) {
t = createTimeout(reject);
// reset function -- allows a one-time timeout different
// from the one original specified
rst = function (tempTtl) {
clearTimeout(t);
t = createTimeout(reject, tempTtl);
}
} else {
// timeToLive = -1 -- allow this promise to run indefinitely
// used while debugging
t = 0;
rst = function () { return; };
}
res = function () {
clearTimeout(t);
resolve();
};
rej = reject;
});
return at = {
promise: p,
resolve: res,
reject: rej,
reset: rst,
timeout: t
};
}
});
/* framework module members... */
test: function (name, fn, options) {
var mod = this; // local reference to framework module since promises
// run code under the window object
var defaultOptions = {
// default max running time is 5 seconds
timeout: 5000
}
options = $$.extend({}, defaultOptions, options);
// remove timeout when debugging is enabled
options.timeout = mod.debugging ? -1 : options.timeout;
// call to QUnit.test()
test(name, function (assert) {
// tell QUnit this is an async test so it doesn't run other tests
// until done() is called
var done = assert.async();
return new Promise(function (resolve, reject) {
console.log("Beginning: " + name);
var at = Promise.asyncTimeout(options.timeout, "Test timed out.");
$$(at).one("timeout", function () {
// assert.fail() is just an extension I made that literally calls
// assert.ok(false, msg);
assert.fail("Test timed out");
});
// run test function
var result = fn.call(mod, assert, at.reset);
// if the test returns a Promise, resolve it before resolving the test promise
if (result && result.constructor === Promise) {
// catch unhandled errors thrown by the test so future tests will run
result.catch(function (error) {
var msg = "Unhandled error occurred."
if (error) {
msg = error.message + "\n" + error.stack;
}
assert.fail(msg);
}).then(function () {
// resolve the timeout Promise
at.resolve();
resolve();
});
} else {
// if test does not return a Promise, simply clear the timeout
// and resolve our test Promise
at.resolve();
resolve();
}
}).then(function () {
// tell QUnit that the test is over so that it can clean up and start the next test
done();
console.log("Ending: " + name);
});
});
}
Si un test expire, ma promesse de délai d'expiration sera assert.fail()
sur le test afin que le test soit marqué comme échoué, ce qui est bien beau, mais le test continue de s'exécuter car le test Promise ( result
) attend toujours de le résoudre.
J'ai besoin d'un bon moyen d'annuler mon test. Je peux le faire en créant un champ sur le module de framework this.cancelTest
ou quelque chose du genre, et en vérifiant de temps en temps (par exemple au début de chaque then()
itération) dans le test s'il faut annuler. Cependant, idéalement, je pourrais utiliser $$(at).on("timeout", /* something here */)
pour effacer les then()
s restants sur ma result
variable, de sorte qu'aucun des autres tests ne soit exécuté.
Est-ce que quelque chose comme ça existe?
Mise à jour rapide
J'ai essayé d'utiliser Promise.race([result, at.promise])
. Ça n'a pas marché.
Mise à jour 2 + confusion
Pour me débloquer, j'ai ajouté quelques lignes avec le mod.cancelTest
/ polling dans l'idée de test. (J'ai également supprimé le déclencheur d'événement.)
return new Promise(function (resolve, reject) {
console.log("Beginning: " + name);
var at = Promise.asyncTimeout(options.timeout, "Test timed out.");
at.promise.catch(function () {
// end the test if it times out
mod.cancelTest = true;
assert.fail("Test timed out");
resolve();
});
// ...
}).then(function () {
// tell QUnit that the test is over so that it can clean up and start the next test
done();
console.log("Ending: " + name);
});
J'ai défini un point d'arrêt dans l' catch
instruction, et il est atteint. Ce qui me trouble maintenant, c'est que la then()
déclaration n'est pas appelée. Des idées?
Mise à jour 3
J'ai compris la dernière chose. fn.call()
lançait une erreur que je n'ai pas détectée, donc la promesse de test était rejetée avant de at.promise.catch()
pouvoir la résoudre.
la source
Prex
bibliothèque pour l'annulation de promesse.Réponses:
Non, au moins pas dans ECMAScript 6. Les promesses (et leurs
then
gestionnaires) ne sont pas annulables par défaut (malheureusement) . Il y a un peu de discussion sur es-discuter (par exemple ici ) sur la façon de faire cela de la bonne manière, mais quelle que soit l'approche qui gagnera, elle n'atterrira pas dans ES6.Le point de vue actuel est que le sous-classement permettra de créer des promesses annulables en utilisant votre propre implémentation ( je ne sais pas si cela fonctionnera bien) .
Tant que le comité de langue n'a pas trouvé le meilleur moyen (ES7 espérons-le?), Vous pouvez toujours utiliser les implémentations Userland Promise, dont beaucoup sont annulées.
La discussion actuelle se trouve dans les versions https://github.com/domenic/cancelable-promise et https://github.com/bergus/promise-cancellation .
la source
kill
le faites et ignorez dans quel état peut-être étrange les effets secondaires ont laissé votre environnement (donc vous le jetez généralement aussi, par exemple toutes les sorties à moitié terminées). Cependant, les fonctions non bloquantes ou asynchrones sont conçues pour fonctionner dans des applications interactives, où vous souhaitez avoir un tel contrôle plus fin sur l'exécution des opérations en cours.Bien qu'il n'y ait pas de manière standard de faire cela dans ES6, il existe une bibliothèque appelée Bluebird pour gérer cela.
Il existe également une méthode recommandée décrite dans la documentation de réaction. Il ressemble à ce que vous avez dans vos 2 et 3 mises à jour.
Tiré de: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
la source
Je suis vraiment surpris que personne ne mentionne
Promise.race
comme candidat pour ceci:la source
cancel()
entraînera toujours l'appel du journal. `` `` const actualPromise = new Promise ((résoudre, rejeter) => {setTimeout (() => {console.log ('réel appelé'); résoudre ()}, 10000)}); `` ``then
à exécuter), pas comment annulersetTimeout
(=>clearTimeout
) ou le code synchrone, où à moins que vous ne mettiez un if après chaque ligne (if (canceled) return
), cela ne peut pas être réalisé. (Ne faites pas ça)Usage:
la source
Il est en fait impossible d'arrêter l'exécution de la promesse, mais vous pouvez détourner le rejet et l'appeler à partir de la promesse elle-même.
Usage:
la source
Il existe quelques bibliothèques npm pour les promesses annulables.
p-annulable https://github.com/sindresorhus/p-cancelable
annulable-promesse https://github.com/alkemics/CancelablePromise
la source
Voici notre implémentation https://github.com/permettez-moi-de-construire/cancellable-promise
Utilisé comme
lequel :
catch
appelTirages et commentaires bienvenus
la source
La promesse peut être annulée à l'aide de
AbortController
.Exemple:
HTML
Exemple en direct
la source
version simple :
donnez simplement la fonction de rejet.
une solution d'emballage (usine)
la solution que j'ai trouvée est de passer un objet cancel_holder. il aura une fonction d'annulation. s'il a une fonction d'annulation, il est annulable.
Cette fonction d'annulation rejette la promesse avec une erreur («annulée»).
Avant de résoudre, rejeter ou on_cancel, empêchez la fonction d'annulation d'être appelée sans raison.
J'ai trouvé pratique de passer l'action d'annulation par injection
la source
Essayez promise-abortable : https://www.npmjs.com/package/promise-abortable
la source
Si votre code est placé dans une classe, vous pouvez utiliser un décorateur pour cela. Vous avez un tel décorateur dans les utils-décorateurs (
npm install --save utils-decorators
). Cela annulera l'appel précédent de la méthode décorée si avant la résolution de l'appel précédent, un autre appel a été effectué pour cette méthode spécifique.https://github.com/vlio20/utils-decorators#cancelprevious-method
la source
Si vous voulez arrêter l'exécution de tous les thens / catchs, vous pouvez le faire en injectant une promesse qui ne se résoudra jamais. Il a probablement des reprocalisations de fuite de mémoire, mais cela résoudra le problème et ne devrait pas causer trop de perte de mémoire dans la plupart des applications.
la source
Définissez une propriété «annulée» sur la promesse pour signaler
then()
etcatch()
quitter tôt. C'est très efficace, en particulier dans les Web Workers qui ont des microtâches existantes en file d'attente dans les promesses desonmessage
gestionnaires.la source
La réponse de @Michael Yagudaev fonctionne pour moi.
Mais la réponse originale n'a pas enchaîné la promesse emballée avec .catch () pour gérer la gestion des rejets, voici mon amélioration en plus de la réponse de @Michael Yagudaev:
la source
Si p est une variable qui contient une promesse, alors
p.then(empty);
devrait rejeter la promesse lorsqu'elle se termine finalement ou si elle est déjà complète (oui, je sais que ce n'est pas la question d'origine, mais c'est ma question). "vide" estfunction empty() {}
. Je ne suis qu'un débutant et j'ai probablement tort, mais ces autres réponses semblent trop compliquées. Les promesses sont censées être simples.la source
Je travaille toujours sur cette idée, mais voici comment j'ai implémenté une promesse annulable en utilisant
setTimeout
comme exemple.L'idée est qu'une promesse est résolue ou rejetée chaque fois que vous l'avez décidé, il devrait donc s'agir de décider quand vous voulez annuler, de satisfaire le critère, puis d'appeler la
reject()
fonction vous-même.Premièrement, je pense qu'il y a deux raisons de terminer une promesse tôt: pour en finir avec (ce que j'ai appelé résoudre ) et pour annuler (que j'ai appelé rejet ). Bien sûr, c'est juste mon sentiment. Bien sûr, il existe une
Promise.resolve()
méthode, mais elle est dans le constructeur lui-même et renvoie une promesse résolue factice. Cetteresolve()
méthode d' instance résout en fait un objet de promesse instancié.En second lieu , vous pouvez heureusement ajouter quoi que ce soit vous aimez à un objet de promesse nouvellement créé avant de le retourner, et je viens d' ajouter
resolve()
et dereject()
méthodes pour le rendre autonome.Troisièmement, l'astuce est de pouvoir accéder à l'exécuteur
resolve
et auxreject
fonctions plus tard, donc je les ai simplement stockés dans un objet simple à partir de la fermeture.Je pense que la solution est simple et je ne vois aucun problème majeur avec elle.
la source