To fajne pytanie, żeby dojść do sedna.
Kiedy to robisz:
verifier(3,4).then(...)
która zwraca nową obietnicę, która wymaga kolejnego cyklu z powrotem do pętli zdarzeń, zanim ta nowo odrzucona obietnica będzie mogła uruchomić następującą .catch()procedurę obsługi. Ten dodatkowy cykl daje następną sekwencję:
verifier(5,4).then(...)
szansa na uruchomienie swojego .then()handlera przed poprzednią linią, .catch()ponieważ znajdował się on już w kolejce zanim .catch()handler z pierwszego dostał się do kolejki i pozycje są uruchamiane z kolejki w kolejności FIFO.
Zwróć uwagę, że jeśli użyjesz .then(f1, f2)formularza zamiast znaku .then().catch(), zostanie on uruchomiony, gdy tego oczekujesz, ponieważ nie ma dodatkowej obietnicy, a zatem nie wymaga dodatkowego tiku:
const verifier = (a, b) =>
new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false)));
verifier(3, 4)
.then((response) => console.log("response (3,4): ", response),
(error) => console.log("error (3,4): ", error)
);
verifier(5, 4)
.then((response) => console.log("response (5,4): ", response))
.catch((error) => console.log("error (5,4): ", error));
Uwaga: oznaczyłem również wszystkie wiadomości etykietami, abyś mógł zobaczyć, z jakiego verifier()wywołania pochodzą, co znacznie ułatwia odczytanie wyników.
Specyfikacja ES6 dotycząca zamawiania oddzwaniania i bardziej szczegółowych wyjaśnień
Specyfikacja ES6 mówi nam, że „zadania” obietnicy (jak wywołuje wywołanie zwrotne z a .then()lub .catch()) są uruchamiane w kolejności FIFO w oparciu o to, kiedy są wstawiane do kolejki zadań. Nie nazywa konkretnie FIFO, ale określa, że nowe zadania są wstawiane na końcu kolejki, a zadania są uruchamiane od początku kolejki. To implementuje porządkowanie FIFO.
PerformPromiseThen (który wykonuje wywołanie zwrotne z .then()) doprowadzi do EnqueueJob, który jest sposobem zaplanowania rzeczywistego uruchomienia procedury obsługi rozwiązywania lub odrzucania. EnqueueJob określa, że oczekujące zadanie jest dodawane z tyłu kolejki zadań. Następnie operacja NextJob ściąga element z początku kolejki. Zapewnia to porządek FIFO w obsłudze zadań z kolejki zadań Promise.
Tak więc w przykładzie w pierwotnym pytaniu otrzymujemy wywołania zwrotne dla verifier(3,4)obietnicy i verifier(5,4)obietnicy wstawionej do kolejki zadań w kolejności, w jakiej zostały uruchomione, ponieważ obie pierwotne obietnice są wykonane. Następnie, gdy interpreter wraca do pętli zdarzeń, najpierw podejmuje verifier(3,4)zadanie. Ta obietnica została odrzucona i nie ma do niej oddzwaniania w verifier(3,4).then(...). A zatem odrzuca verifier(3,4).then(...)zwróconą obietnicę i powoduje verifier(3,4).then(...).catch(...)wstawienie procedury obsługi do jobQueue.
Następnie wraca do pętli zdarzeń i następnym zadaniem, które pobiera z jobQueue, jest verifier(5, 4)zadanie. Ma rozwiązaną obietnicę i procedurę obsługi rozstrzygania, więc wywołuje tę procedurę obsługi. Powoduje response (5,4):to pokazanie wyjścia.
Następnie wraca do pętli zdarzeń, a następne zadanie, które pobiera z jobQueue, to verifier(3,4).then(...).catch(...)zadanie, w którym je uruchamia, co powoduje error (3,4)wyświetlenie danych wyjściowych.
Dzieje się tak dlatego, że .catch()w pierwszym łańcuchu jest o jeden poziom obietnicy głębiej w swoim łańcuchu niż .then()w drugim łańcuchu, który powoduje zgłoszenie kolejności. Dzieje się tak dlatego, że łańcuchy obietnic przechodzą z jednego poziomu na drugi przez kolejkę zadań w kolejności FIFO, a nie synchronicznie.
Ogólne zalecenia dotyczące polegania na tym poziomie szczegółowości harmonogramu
FYI, generalnie, staram się pisać kod, który nie zależy od tego poziomu szczegółowej wiedzy o synchronizacji. Chociaż jest to ciekawe i czasami przydatne do zrozumienia, jest to kruchy kod, ponieważ prosta pozornie nieszkodliwa zmiana w kodzie może prowadzić do zmiany względnego czasu. Tak więc, jeśli synchronizacja między dwoma łańcuchami jest krytyczna, wolałbym napisać kod w sposób, który wymusza synchronizację w taki sposób, w jaki chcę, niż polegać na tym poziomie szczegółowego zrozumienia.