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.
quelle
Prex
Bibliothek für die Stornierung von Versprechungen.Antworten:
Nein, zumindest nicht in ECMAScript 6. Versprechen (und ihre
then
Handler) können (leider) standardmäßig nicht storniert werden . Es gibt einige Diskussionen über es-Diskussion (z. B. hier ) darüber, wie dies richtig gemacht werden kann, aber welcher Ansatz auch immer gewinnen wird, wird nicht in ES6 landen.Der aktuelle Standpunkt ist, dass Unterklassen es ermöglichen, stornierbare Versprechen mit Ihrer eigenen Implementierung zu erstellen (nicht sicher, wie gut das funktionieren wird) .
Bis das Sprachkomitee den besten Weg gefunden hat (hoffentlich ES7?), Können Sie weiterhin Userland Promise-Implementierungen verwenden, von denen viele nicht mehr funktionieren .
Die aktuelle Diskussion findet sich in den Entwürfen https://github.com/domenic/cancelable-promise und https://github.com/bergus/promise-cancellation .
quelle
kill
ignorieren es in dem möglicherweise seltsamen Zustand, in dem die Nebenwirkungen Ihre Umgebung belassen haben (also werfen Sie das normalerweise auch weg, z. B. halbfertige Ausgaben). Nicht blockierende oder asynchrone Funktionen funktionieren jedoch in interaktiven Anwendungen, in denen Sie eine genauere Kontrolle über die Ausführung laufender Vorgänge wünschen.Während es in ES6 keine Standardmethode gibt, gibt es eine Bibliothek namens Bluebird , um dies zu handhaben.
Es gibt auch einen empfohlenen Weg, der als Teil der Reaktionsdokumentation beschrieben wird. Es sieht ähnlich aus wie in Ihrem 2. und 3. Update.
Entnommen aus: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
quelle
Ich bin wirklich überrascht, dass niemand
Promise.race
als Kandidat dafür erwähnt:quelle
cancel()
weiterhin aufgerufen. `` `const actualPromise = neues Versprechen ((auflösen, ablehnen) => {setTimeout (() => {console.log ('tatsächlich aufgerufen'); auflösen ()}, 10000)}); `` `then
s beenden , um ausgeführt zu werden), nicht wie mansetTimeout
(=>clearTimeout
) oder synchronen Code storniert , wobeiif (canceled) return
dies nur erreicht werden kann , wenn Sie nach jeder Zeile ( ) ein Wenn setzen . (Tun Sie dies nicht)Verwendung:
quelle
Es ist tatsächlich unmöglich, die Ausführung des Versprechens zu stoppen, aber Sie können die Ablehnung entführen und sie vom Versprechen selbst abrufen.
Verwendung:
quelle
Es gibt einige npm-Bibliotheken für stornierbare Versprechen.
p-cancelable https://github.com/sindresorhus/p-cancelable
Stornierbares Versprechen https://github.com/alkemics/CancelablePromise
quelle
Hier ist unsere Implementierung https://github.com/permettez-moi-de-construire/cancellable-promise
Gebraucht wie
welche :
catch
Anrufs weitere Stornierungen vornehmenPulls und Kommentare sind willkommen
quelle
Versprechen kann mit Hilfe von storniert werden
AbortController
.Beispiel:
Html
Live-Beispiel
quelle
einfache Version :
Geben Sie einfach die Ablehnungsfunktion aus.
eine Wraper-Lösung (Fabrik)
Die Lösung, die ich gefunden habe, besteht darin, ein cancel_holder-Objekt zu übergeben. es wird eine Abbruchfunktion haben. Wenn es eine Abbruchfunktion hat, kann es abgebrochen werden.
Diese Abbruchfunktion lehnt das Versprechen mit Fehler ab ('abgebrochen').
Vor dem Auflösen, Ablehnen oder on_cancel verhindern Sie, dass die Abbruchfunktion ohne Grund aufgerufen wird.
Ich habe es als zweckmäßig empfunden, die Abbruchaktion durch Injektion zu bestehen
quelle
Versuchen Sie, Versprechen abzubrechen : https://www.npmjs.com/package/promise-abortable
quelle
Wenn Ihr Code in einer Klasse platziert ist, können Sie dafür einen Dekorateur verwenden. Sie haben einen solchen Dekorateur in den Utils-Dekoratoren (
npm install --save utils-decorators
). Der vorherige Aufruf der dekorierten Methode wird abgebrochen, wenn vor dem Auflösen des vorherigen Aufrufs ein weiterer Aufruf für diese bestimmte Methode erfolgt ist.https://github.com/vlio20/utils-decorators#cancelprevious-method
quelle
Wenn Sie verhindern möchten, dass alle Thens / Catches ausgeführt werden, können Sie dies tun, indem Sie ein Versprechen einfügen, das sich niemals auflösen lässt. Es hat wahrscheinlich Speicherleck-Reprocusionen, aber es wird das Problem beheben und sollte in den meisten Anwendungen nicht zu viel Speicherverschwendung verursachen.
quelle
Stellen Sie im Versprechen eine "abgebrochene" Eigenschaft ein, um zu signalisieren
then()
undcatch()
vorzeitig zu beenden. Dies ist sehr effektiv, insbesondere bei Web-Workern, bei denen vorhandene Mikrotasks in Promises von Handlern in die Warteschlange gestellt wurdenonmessage
.quelle
@ Michael Yagudaevs Antwort funktioniert für mich.
Aber die ursprüngliche Antwort hat das verpackte Versprechen nicht mit .catch () verkettet, um die Bearbeitung von Ablehnungen zu handhaben. Hier ist meine Verbesserung zusätzlich zu der Antwort von @Michael Yagudaev:
quelle
Wenn p eine Variable ist, die ein Versprechen enthält,
p.then(empty);
sollte das Versprechen verworfen werden, wenn es schließlich abgeschlossen ist oder wenn es bereits abgeschlossen ist (ja, ich weiß, dass dies nicht die ursprüngliche Frage ist, aber es ist meine Frage). "leer" istfunction empty() {}
. Ich bin nur ein Anfänger und wahrscheinlich falsch, aber diese anderen Antworten scheinen zu kompliziert. Versprechen sollen einfach sein.quelle
Ich arbeite noch an dieser Idee, aber hier ist, wie ich ein stornierbares Versprechen mit implementiert habe
setTimeout
Beispiel .Die Idee ist, dass ein Versprechen gelöst oder abgelehnt wird, wann immer Sie es entschieden haben. Es sollte also eine Frage der Entscheidung sein, wann Sie stornieren möchten, das Kriterium erfüllen und dann die
reject()
Funktion selbst aufrufen .Erstens denke ich, dass es zwei Gründe gibt, ein Versprechen vorzeitig zu beenden: es zu Ende zu bringen (was ich als Entschlossenheit bezeichnet habe ) und abzubrechen (was ich als Ablehnung bezeichnet habe ). Das ist natürlich nur mein Gefühl. Natürlich gibt es eine
Promise.resolve()
Methode, aber sie befindet sich im Konstruktor selbst und gibt ein Dummy-gelöstes Versprechen zurück. Diese Instanzmethoderesolve()
löst tatsächlich ein instanziiertes Versprechungsobjekt auf.Zweitens können Sie gerne alles , was Sie wie in ein neu erstelltes Versprechen Objekt hinzufügen , bevor Sie es zurück, und so habe ich gerade hinzugefügt haben
resolve()
undreject()
Methoden zu machen , in sich abgeschlossen.Drittens besteht der Trick darin, später auf den Executor
resolve
und diereject
Funktionen zugreifen zu können , sodass ich sie einfach in einem einfachen Objekt innerhalb des Verschlusses gespeichert habe.Ich denke, die Lösung ist einfach und ich kann keine größeren Probleme damit sehen.
quelle