Nowicjusz w node.js, jaka jest przewaga uzyskana dzięki wywołaniom zwrotnym nad zdarzeniami?

24

Jestem początkującym programistą JavaScripter i nie mam prawdziwej wiedzy o tym, co dzieje się w silniku V8.

Powiedziawszy to, bardzo lubię moje wczesne wyprawy do środowiska node.js, ale okazuje się, że ciągle używam events.EventEmitter () jako środka do emitowania globalnych zdarzeń, dzięki czemu mogę tak ustrukturyzować swoje programy, aby pasowały do ​​obserwatora-obserwatora wzorzec podobny do tego, co bym napisał w programie Objective-C lub Python.

Zawsze robię takie rzeczy:

var events = require('events');

var eventCenter = new events.EventEmitter();

eventCenter.on('init', function() {
    var greeting = 'Hello World!';
    console.log('We're in the init function!);
    eventCenter.emit('secondFunction', greeting);
});

eventCenter.on('secondFunction', function(greeting) {
        console.log('We're in the second function!);
        console.log(greeting);
    eventCenter.emit('nextFunction');
});

eventCenter.on('nextFunction', function {
    /* do stuff */
});

eventCenter.emit('init');

W efekcie po prostu konstruuję kod „asynchroniczny” node.js w kodzie, który wykonuje czynności w oczekiwanej przeze mnie kolejności, zamiast tego w pewnym sensie „koduję wstecz”. Czy byłaby jakaś różnica w robieniu tego w sposób zwrotny, pod względem wydajności lub filozofii? Czy lepiej robić to samo przy użyciu wywołań zwrotnych zamiast zdarzeń?

Biec truchtem
źródło
Jeśli zauważysz, że Twój kod jest nieczytelny z powodu częstego korzystania z wywołań zwrotnych, zastanów się nad skorzystaniem z frameworka, aby zakończyć korzystanie z wywołań zwrotnych. To wymaga trochę regulacji, ale obietnice często mogą rozwiązać brzydkie wywołania zwrotne. Jeśli używasz JQuery, możesz użyć opcji Odroczony . Jednym z najprostszych ram obietnicy jest RSVP .
Brian
Jestem zdezorientowany, nie widzę nic asynchronicznego w twoim kodzie, tylko wywołania funkcji napisane w zbyt skomplikowany sposób.
svick

Odpowiedzi:

27

Zaletą wywołań zwrotnych jest to, że nie ma tam stanu globalnego, a przekazywanie im parametrów jest banalne. Jeśli masz funkcję download(URL, callback: (FileData)->void), możesz wiedzieć, że jest to samodzielna funkcja wyższego rzędu, która zasadniczo pozwala skonstruować funkcję „weź to i zrób to”. Możesz być pewien, że przepływ kodu jest dokładnie taki, jak się spodziewasz, ponieważ nikt inny nawet nie ma dojścia do tego wywołania zwrotnego, a to wywołanie zwrotne nie wie nic poza parametrami podanymi przez funkcję rodzica. Dzięki temu jest modułowy i łatwy w testowaniu.

Jeśli chcesz teraz pobrać 5 plików równolegle i wykonywać różne czynności, wystarczy uruchomić tylko pięć z tych funkcji za pomocą odpowiednich funkcji wywołania zwrotnego. W języku z dobrą składnią funkcji anonimowych może to być niezwykle potężne.

Z drugiej strony zdarzenia są bardziej przeznaczone do powiadamiania 1..*użytkowników o zmianie stanu. Jeśli uruchomisz zdarzenie „zakończenie pobierania” download(URL), które uruchamia się i processDownload()wie, gdzie znaleźć dane, wiążesz swoje implementacje rzeczy z większą liczbą stanów. Jak teraz równolegle pobierać pliki? Jak inaczej radzisz sobie z różnymi plikami do pobrania? Jest download(URL, eventId)elegancki? Jest downloadAndDoThing(URL)elegancki? Prawie wcale.

Oczywiście, jak w przypadku wszystkich rzeczy, dokonujecie kompromisów. Zagnieżdżone wywołania zwrotne mogą sprawiać, że kolejność wykonywania kodu jest bardziej myląca, a brak łatwej globalnej dostępności sprawia, że ​​jest to zły wybór dla wszystkiego, co ma 1..*związek między producentem a konsumentem. Trudniej jest ci przekazywać dane, ale jeśli możesz uniknąć dodatkowego stanu, to i tak jest to z reguły korzyść.

Bez względu. Kodujesz w node.js, gdzie wywołania zwrotne są idiomatyczne, a język i biblioteki zostały zaprojektowane z myślą o ich użyciu. Niezależnie od tego, czy widzisz zalety takiego czy innego projektu, myślę, że prawie zawsze jest prawdą, że pisanie idiomatycznego kodu w dowolnym języku będzie miało znacznie większe wsparcie i ułatwi ci życie, niż próbując go obejść.

Phoshi
źródło
2

Nigdy nie korzystałem ze zdarzeń w NodeJS, ale ogólnie rzecz biorąc, do czego używam zdarzeń JS po stronie klienta, oznacza to, że coś się wydarzyło, np. AppointmentBookedEvent, aby różne części widoku SPA mogły na to zareagować (pokaż to na osi czasu, załaduj to w panelu itp.).
Ale używanie zdarzeń do sygnalizowania zakończenia metody może być niebezpieczną drogą do podróży. Nie można polegać na zdarzeniach, które przybywają w tej samej kolejności, w jakiej zostały wystrzelone, co może prowadzić do różnego rodzaju przypadkowości ...
Nie ma nic złego w korzystaniu ze zdarzeń, ale istnieje pewien poziom szczegółowości, którego nie należy obniżać ; jeśli trzymasz się wydarzeń związanych z domeną, nie ma sprawy. Ale powiadomienie o zakończeniu metod jest zbyt szczegółowe.

Stefan Billiet
źródło
0

Możesz zbadać użycie zdarzeń poza obiektem, który je emituje. W twoim przykładzie eventCenterwydaje się zarówno emitować, jak i obsługiwać własne zdarzenia. Zastanów się, jak może zmienić się struktura Twojej aplikacji, jeśli inne obiekty zaczną obsługiwać zdarzenia.

eventCenterMoże więc emitować „init”, który inne obiekty mogłyby obsługiwać w celu wykonania kodu startowego itp.

Dan1701
źródło