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.
Prex
bibliothèque pour l'annulation de promesse.