Robię testy jednostkowe. Środowisko testowe ładuje stronę do iFrame, a następnie uruchamia potwierdzenia na tej stronie. Przed rozpoczęciem każdego testu tworzę element, Promise
który ustawia onload
zdarzenie iFrame do wywołania resolve()
, ustawia iFrame src
i zwraca obietnicę.
Mogę więc po prostu zadzwonić loadUrl(url).then(myFunc)
i poczekać na załadowanie strony przed wykonaniem tego, co myFunc
jest.
Używam tego rodzaju wzorca w moich testach (nie tylko do ładowania adresów URL), głównie po to, aby umożliwić wprowadzenie zmian w DOM (np. Naśladuj kliknięcie przycisku i czekaj, aż elementy div się ukryją i pokażą).
Wadą tego projektu jest to, że ciągle piszę anonimowe funkcje z kilkoma wierszami kodu. Ponadto, mimo że mam obejście (QUnit assert.async()
), funkcja testowa, która definiuje obietnice, kończy się przed uruchomieniem obietnicy.
Zastanawiam się, czy istnieje sposób, aby uzyskać wartość z a Promise
lub czekać (blok / uśpienie), aż zostanie rozwiązany, podobnie jak w .NET IAsyncResult.WaitHandle.WaitOne()
. Wiem, że JavaScript jest jednowątkowy, ale mam nadzieję, że nie oznacza to, że funkcja nie może dać.
Zasadniczo, czy istnieje sposób na wyplucie wyników we właściwej kolejności?
function kickOff() {
return new Promise(function(resolve, reject) {
$("#output").append("start");
setTimeout(function() {
resolve();
}, 1000);
}).then(function() {
$("#output").append(" middle");
return " end";
});
};
function getResultFrom(promise) {
// todo
return " end";
}
var promise = kickOff();
var result = getResultFrom(promise);
$("#output").append(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>
źródło
.then(fnAppend.bind(myDiv))
, które mogą znacznie skrócić anons.Odpowiedzi:
Obecna generacja Javascript w przeglądarkach nie ma
wait()
lub,sleep()
która pozwala na działanie innych rzeczy. Więc po prostu nie możesz zrobić tego, o co prosisz. Zamiast tego ma operacje asynchroniczne, które wykonają swoje zadanie, a następnie zadzwonią po zakończeniu (tak jak korzystałeś z obietnic).Częściowo wynika to z pojedynczej wątkowości JavaScript. Jeśli pojedynczy wątek się kręci, żaden inny JavaScript nie może być wykonywany, dopóki ten wirujący wątek nie zostanie zakończony. ES6 wprowadza
yield
i generatory, które pozwolą na kilka takich sztuczek kooperacyjnych, ale jesteśmy całkiem spora od możliwości użycia ich w szerokiej próbce zainstalowanych przeglądarek (mogą być używane w niektórych programach po stronie serwera, gdzie kontrolujesz silnik JS który jest używany).Uważne zarządzanie kodem opartym na obietnicy może kontrolować kolejność wykonywania wielu operacji asynchronicznych.
Nie jestem pewien, czy dokładnie rozumiem, jaką kolejność próbujesz osiągnąć w swoim kodzie, ale możesz zrobić coś takiego, używając istniejącej
kickOff()
funkcji, a następnie dołączyć.then()
do niej procedurę obsługi po wywołaniu jej:To zwróci dane wyjściowe w gwarantowanej kolejności - na przykład:
Aktualizacja w 2018 r. (Trzy lata po napisaniu tej odpowiedzi):
Jeśli dokonasz transpozycji kodu lub uruchomisz go w środowisku obsługującym funkcje ES7, takie jak
async
iawait
, możesz teraz użyć funkcji,await
aby kod „pojawił się” i czekał na wynik obietnicy. Nadal rozwija się obiecująco. Nadal nie blokuje całego Javascript, ale umożliwia pisanie sekwencyjnych operacji w bardziej przyjaznej składni.Zamiast robić rzeczy w ES6:
Możesz to zrobić:
źródło
then()
jest tym, co robię. Po prostu nie lubię ciągle pisaćfunction() { ... }
. Zaśmieca kod.(foo) => { ... }
zamiastfunction(foo) { ... }
foo => single.expression(here)
aby pozbyć się nawiasów klamrowych i okrągłych.async
iawait
składnię jako opcję, która jest teraz dostępna w node.js oraz w nowoczesnych przeglądarkach lub przez transpilery.Jeśli używasz ES2016, możesz użyć
async
iawait
i zrobić coś takiego:Jeśli używasz ES2015, możesz użyć Generatorów . Jeśli nie podoba ci się składnia, możesz ją wyodrębnić za pomocą
async
funkcji narzędzia, jak wyjaśniono tutaj .Jeśli używasz ES5, prawdopodobnie będziesz chciał, aby biblioteka taka jak Bluebird zapewniła Ci większą kontrolę.
Wreszcie, jeśli środowisko uruchomieniowe obsługuje ES2015, już kolejność wykonywania może zostać zachowana z równoległością przy użyciu funkcji Fetch Injection .
źródło
async
/await
.async
/await
to tylko kolejna składnia dlaPromise.then
.async
nie będzie czekać ... chyba że daje użyciuawait
. Przykład ES2016 / ES7 nadal nie może zostać uruchomiony SO, więc oto kod, który napisałem trzy lata temu, demonstrujący to zachowanie.Inną opcją jest użycie Promise.all, aby poczekać na rozwiązanie szeregu obietnic. Poniższy kod pokazuje, jak czekać na spełnienie wszystkich obietnic, a następnie radzić sobie z wynikami, gdy wszystkie są gotowe (ponieważ wydawało się, że to jest cel pytania); Jednak start może oczywiście wyprowadzić, zanim middle zostanie rozwiązany, po prostu dodając ten kod przed wywołaniem resell.
źródło
Możesz to zrobić ręcznie. (Wiem, że to nie jest świetne rozwiązanie, ale…) używaj
while
pętli, dopókiresult
nie będzie miała wartościźródło