Czy istnieje metoda czyszczenia .then
s Promise
instancji JavaScript ?
Napisałem framework testowy JavaScript w oparciu o QUnit . Struktura uruchamia testy synchronicznie, uruchamiając każdy z nich w Promise
. (Przepraszam za długość tego bloku kodu. Skomentowałem go najlepiej, jak potrafiłem, więc jest mniej uciążliwy).
/* 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);
});
});
}
Jeśli test przekroczy limit czasu, moja obietnica przekroczenia limitu czasu włączy assert.fail()
się do testu, tak że test zostanie oznaczony jako nieudany, co jest w porządku, ale test jest kontynuowany, ponieważ test Promise ( result
) nadal czeka, aby go rozwiązać.
Potrzebuję dobrego sposobu na anulowanie testu. Mogę to zrobić, tworząc pole w module frameworka this.cancelTest
lub czymś w tym rodzaju i sprawdzając co jakiś czas (np. Na początku każdej then()
iteracji) w ramach testu, czy się anulować. Jednak w idealnym przypadku mógłbym użyć $$(at).on("timeout", /* something here */)
do wyczyszczenia pozostałych then()
s na mojej result
zmiennej, aby żadna z pozostałych testów nie została uruchomiona.
Czy coś takiego istnieje?
Szybka aktualizacja
Próbowałem użyć Promise.race([result, at.promise])
. To nie zadziałało.
Aktualizacja 2 + zamieszanie
Aby mnie odblokować, dodałem kilka wierszy z mod.cancelTest
/ odpytywaniem w pomyśle testowym. (Usunąłem też wyzwalacz zdarzenia).
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);
});
Ustawiłem punkt przerwania w catch
instrukcji i jest trafiony. Co mnie teraz dezorientuje, to fakt, że to then()
stwierdzenie nie jest wywoływane. Pomysły?
Zaktualizuj 3
Zrozumiałem ostatnią rzecz. fn.call()
wyrzucał błąd, którego nie złapałem, więc obietnica testowa została odrzucona, zanim at.promise.catch()
mogła go rozwiązać.
źródło
Prex
biblioteki do anulowania obietnicy.Odpowiedzi:
Nie. Przynajmniej nie w ECMAScript 6. Obietnice (i ich
then
opiekunowie) domyślnie nie podlegają anulowaniu (niestety) . Trwa dyskusja na temat es-dyskusji (np. Tutaj ) o tym, jak to zrobić we właściwy sposób, ale jakiekolwiek podejście wygra, nie wyląduje w ES6.Obecne stanowisko jest takie, że tworzenie podklas pozwoli na tworzenie anulowalnych obietnic przy użyciu własnej implementacji (nie wiem, jak dobrze to zadziała) .
Dopóki komitet językowy nie wymyśli najlepszego sposobu (miejmy nadzieję, że ES7?) Nadal można używać implementacji platformy użytkownika Promise, z których wiele jest anulowanych.
Bieżąca dyskusja znajduje się w wersjach roboczych https://github.com/domenic/cancelable-promise i https://github.com/bergus/promise-cancellation .
źródło
kill
go ignorujesz, w jakim być może dziwnym stanie, w jakim efekty uboczne pozostawiły twoje środowisko (więc zazwyczaj po prostu to wyrzucasz, np. Niedokończone wyjścia). Funkcje nieblokujące lub asynchroniczne są jednak zbudowane do pracy w aplikacjach interaktywnych, w których chcesz mieć lepszą kontrolę nad wykonywaniem bieżących operacji.Chociaż w ES6 nie ma standardowego sposobu na zrobienie tego, istnieje biblioteka o nazwie Bluebird, która to umożliwia.
Istnieje również zalecany sposób opisany jako część dokumentacji reagowania. Wygląda podobnie do tego, co masz w 2 i 3 aktualizacjach.
Zaczerpnięte z: https://facebook.github.io/react/blog/2015/12/16/ismounted-antipattern.html
źródło
Jestem naprawdę zaskoczony, że nikt nie wymienia
Promise.race
jako kandydata do tego:źródło
cancel()
nadal spowoduje wywołanie dziennika. `` `` const rzeczywistePromise = new Promise ((rozstrzygnij, odrzuć) => {setTimeout (() => {console.log ('rzeczywiste wezwanie'); rozwiąż ()}, 10000)}); ``then
do wykonania), a nie jak anulowaćsetTimeout
(=>clearTimeout
) lub kod synchroniczny, gdzie nie możnaif (canceled) return
tego osiągnąć , chyba że umieścisz if po każdej linii ( ). (Nie rób tego)Stosowanie:
źródło
W rzeczywistości niemożliwe jest zatrzymanie realizacji obietnicy, ale możesz przejąć odrzucenie i wywołać to z samej obietnicy.
Stosowanie:
źródło
Istnieje kilka bibliotek npm do anulowania obietnic.
p-anulowalne https://github.com/sindresorhus/p-cancelable
obietnica anulowania https://github.com/alkemics/CancelablePromise
źródło
Oto nasza realizacja https://github.com/permettez-moi-de-construire/cancellable-promise
Używane jak
który :
catch
połączenia wewnętrznegoMile widziane ściągnięcia i komentarze
źródło
Obietnicę można anulować za pomocą
AbortController
.Przykład:
HTML
Przykład na żywo
źródło
prosta wersja :
po prostu daj funkcję odrzucania.
rozwiązanie do pakowania (fabryka)
rozwiązaniem, które znalazłem, jest przekazanie obiektu cancel_holder. będzie miał funkcję anulowania. jeśli ma funkcję anulowania, można ją anulować.
Ta funkcja anulowania odrzuca obietnicę z błędem („anulowano”).
Przed rozwiązaniem, odrzucaj lub on_cancel zapobiega wywoływaniu funkcji anulowania bez powodu.
Zauważyłem, że wygodnie jest przekazać akcję anulowania przez wstrzyknięcie
źródło
Wypróbuj możliwość anulowania obietnicy : https://www.npmjs.com/package/promise-abortable
źródło
Jeśli twój kod jest umieszczony w klasie, możesz użyć do tego dekoratora. Masz takiego dekoratora w narzędziu -dekoratorach (
npm install --save utils-decorators
). Anuluje poprzednie wywołanie dekorowanej metody, jeśli przed rozstrzygnięciem poprzedniego wywołania zostało wykonane inne wywołanie tej konkretnej metody.https://github.com/vlio20/utils-decorators#cancelprevious-method
źródło
Jeśli chcesz powstrzymać wykonywanie wszystkich następnie / złapań, możesz to zrobić, wstrzykując obietnicę, która nigdy się nie rozwiąże. Prawdopodobnie ma zmiany orientacji wycieku pamięci, ale rozwiąże problem i nie powinno powodować zbytniego marnowania pamięci w większości aplikacji.
źródło
Ustaw właściwość „anulowana” w Obietnicy, aby zasygnalizować
then()
icatch()
wyjść wcześniej. Jest bardzo skuteczny, szczególnie w przypadku pracowników sieci, którzy mają istniejące mikrozadania w kolejce w Obietnice odonmessage
obsługi.źródło
Odpowiedź @Michaela Yagudaeva działa dla mnie.
Ale pierwotna odpowiedź nie wiązała zapakowanej obietnicy z .catch () w celu obsługi odrzutów, oto moje ulepszenie w stosunku do odpowiedzi @Michael Yagudaev:
źródło
Jeśli p jest zmienną zawierającą obietnicę,
p.then(empty);
należy ją odrzucić, gdy w końcu się zakończy lub jeśli jest już zakończona (tak, wiem, że to nie jest oryginalne pytanie, ale jest to moje pytanie). „pusty” jestfunction empty() {}
. Jestem tylko początkującym i prawdopodobnie się mylę, ale te inne odpowiedzi wydają się zbyt skomplikowane. Obietnice mają być proste.źródło
Nadal pracuję nad tym pomysłem, ale oto jak zaimplementowałem anulowalną obietnicę za pomocą
setTimeout
na przykładzie.Chodzi o to, że obietnica jest rozwiązana lub odrzucona, gdy tylko zdecydujesz, że tak jest, więc powinno być kwestią decyzji, kiedy chcesz anulować, spełnienia kryterium, a następnie
reject()
samodzielnego wywołania funkcji.Po pierwsze, myślę, że są dwa powody, dla których warto dokończyć obietnicę wcześniej: aby ją zrealizować i z nią skończyć (co nazwałem rozwiązaniem ) oraz anulować (które nazwałem odrzuceniem ). Oczywiście, to tylko moje odczucie. Oczywiście istnieje
Promise.resolve()
metoda, ale znajduje się ona w samym konstruktorze i zwraca fikcyjną rozwiązaną obietnicę. Taresolve()
metoda instancji w rzeczywistości rozwiązuje instancję obiektu obietnicy.Po drugie, możesz szczęśliwie dodać wszystko, co chcesz, do nowo utworzonego obiektu obietnicy, zanim go zwrócisz, dlatego właśnie dodałem
resolve()
ireject()
metody, aby uczynić go samowystarczalnym.Po trzecie, sztuczka polega na uzyskaniu dostępu do modułu wykonawczego
resolve
ireject
funkcji później, więc po prostu zapisałem je w prostym obiekcie z poziomu zamknięcia.Myślę, że rozwiązanie jest proste i nie widzę w nim większych problemów.
źródło