Gibt es eine Methode zum Löschen des .then
s einer JavaScript- Promise
Instanz?
Ich habe ein JavaScript-Testframework über QUnit geschrieben . Das Framework führt Tests synchron aus, indem jeder in a ausgeführt wird Promise
. (Entschuldigen Sie die Länge dieses Codeblocks. Ich habe ihn so gut wie möglich kommentiert, damit er sich weniger langweilig anfühlt.)
/* 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);
});
});
}
Wenn bei einem Test eine Zeitüberschreitung auftritt, wird mein Zeitüberschreitungsversprechen assert.fail()
für den Test aktiviert, sodass der Test als fehlgeschlagen markiert wird. Dies ist alles in Ordnung und gut, aber der Test wird weiterhin ausgeführt, da das Testversprechen ( result
) noch auf die Lösung wartet.
Ich brauche einen guten Weg, um meinen Test abzubrechen. Ich kann dies tun, indem ich ein Feld auf dem Framework-Modul this.cancelTest
oder etwas anderem erstelle und von Zeit zu Zeit (z. B. zu Beginn jeder then()
Iteration) innerhalb des Tests überprüfe, ob ich abbrechen soll. Im Idealfall könnte ich jedoch $$(at).on("timeout", /* something here */)
die verbleibenden then()
s in meiner result
Variablen löschen , damit keiner der restlichen Tests ausgeführt wird.
Gibt es so etwas?
Schnelles Update
Ich habe es versucht Promise.race([result, at.promise])
. Es hat nicht funktioniert.
Update 2 + Verwirrung
Um mich zu entsperren, habe ich mod.cancelTest
innerhalb der Testidee einige Zeilen mit dem / polling hinzugefügt . (Ich habe auch den Ereignisauslöser entfernt.)
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);
});
Ich habe einen Haltepunkt in der catch
Anweisung festgelegt, der getroffen wird. Was mich jetzt verwirrt, ist, dass die then()
Anweisung nicht aufgerufen wird. Ideen?
Update 3
Ich habe das Letzte herausgefunden. fn.call()
warf einen Fehler, den ich nicht abfing, so dass das Testversprechen abgelehnt wurde, bevor at.promise.catch()
es behoben werden konnte.
Prex
Bibliothek für die Stornierung von Versprechungen.