setImmediate vs. nextTick

Węzeł.js wersja 0.10 została wydana dzisiaj i wprowadziła setImmediate. Interfejs API zmienia dokumentację sugerując użycie go podczas rekurencyjnych wywołań nextTick.

Z tego, co mówi MDN wydaje się bardzo podobne do process.nextTick.

Kiedy należy stosować nextTick i kiedy należy stosować setImmediate?

Author: Michał Perłakowski, 2013-03-12

6 answers

Użyj setImmediate, jeśli chcesz ustawić w kolejce funkcję za wywołaniami zdarzeń We/Wy, które są już w kolejce zdarzeń. Użyj process.nextTick, aby efektywnie ustawić funkcję w kolejce zdarzeń, tak aby była wykonywana natychmiast po zakończeniu bieżącej funkcji.

Więc w przypadku, gdy próbujesz przerwać długo działające zadanie związane z procesorem za pomocą rekurencji, powinieneś teraz użyć setImmediate zamiast process.nextTick do kolejkowania następnej iteracji, ponieważ w przeciwnym razie wywołania zdarzeń we/wy nie zostałyby wywołane masz szansę biegać między iteracjami.

 441
Author: JohnnyHK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-13 14:10:37

Jako ilustracja

import fs from 'fs';
import http from 'http';

const options = {
  host: 'www.stackoverflow.com',
  port: 80,
  path: '/index.html'
};

describe('deferredExecution', () => {
  it('deferredExecution', (done) => {
    console.log('Start');
    setTimeout(() => console.log('TO1'), 0);
    setImmediate(() => console.log('IM1'));
    process.nextTick(() => console.log('NT1'));
    setImmediate(() => console.log('IM2'));
    process.nextTick(() => console.log('NT2'));
    http.get(options, () => console.log('IO1'));
    fs.readdir(process.cwd(), () => console.log('IO2'));
    setImmediate(() => console.log('IM3'));
    process.nextTick(() => console.log('NT3'));
    setImmediate(() => console.log('IM4'));
    fs.readdir(process.cwd(), () => console.log('IO3'));
    console.log('Done');
    setTimeout(done, 1500);
  });
});

Da następujące Wyjście

Start
Done
NT1
NT2
NT3
TO1
IO2
IO3
IM1
IM2
IM3
IM4
IO1
Mam nadzieję, że to pomoże zrozumieć różnicę.
 40
Author: DraganS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-03 18:44:48

W komentarzach w odpowiedzi nie stwierdza się wprost, że nextTick przeszedł z Makrosemantyki na Mikrosemantykę.

Przed node 0.9 (kiedy wprowadzono setImmediate), nextTick działał na początku następnego callstacka.

Od node 0.9, nextTick działa na końcu istniejącego callstacka, podczas gdy setImmediate jest na początku następnego callstacka

Sprawdź https://github.com/YuzuJS/setImmediate do narzędzi i detali

 29
Author: Jay Day Zee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-27 01:44:10

Myślę, że mogę to ładnie zilustrować. Ponieważ nextTick jest wywoływana pod koniec bieżącej operacji, wywołanie jej rekurencyjnie może zakończyć się zablokowaniem pętli zdarzenia przed kontynuowaniem. setImmediate rozwiązuje to poprzez uruchomienie w fazie sprawdzania pętli zdarzeń, umożliwiając normalne kontynuowanie pętli zdarzeń.

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘

Źródło: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

Zauważ, że faza kontroli jest natychmiast po fazie ankiety. Dzieje się tak dlatego, że Faza ankiety i połączenia zwrotne We / Wy są najbardziej prawdopodobnymi miejscami, do których będą uruchamiane połączenia setImmediate. Więc najlepiej, żeby większość tych wywołań była natychmiastowa, ale nie tak natychmiastowa jak nextTick, która jest sprawdzana po każdej operacji i technicznie istnieje poza pętlą zdarzeń.

Spójrzmy na mały przykład różnicy między setImmediate i process.nextTick:

function step(iteration) {
  if (iteration === 10) return;
  setImmediate(() => {
    console.log(`setImmediate iteration: ${iteration}`);
    step(iteration + 1); // Recursive call from setImmediate handler.
  });
  process.nextTick(() => {
    console.log(`nextTick iteration: ${iteration}`);
  });
}
step(0);

Załóżmy, że właśnie uruchomiliśmy ten program i przechodzimy przez pierwszą iterację pętli zdarzeń. Informatyka wywoła funkcję step z iteracją zero. Następnie zarejestruje dwa manipulatory, jeden dla setImmediate i jeden dla process.nextTick. Następnie rekurencyjnie wywołujemy tę funkcję z obsługi setImmediate, która będzie działać w następnej fazie sprawdzania. Funkcja obsługi nextTick uruchomi się pod koniec bieżącej operacji przerywającej pętlę zdarzeń, więc nawet jeśli została zarejestrowana jako druga, to faktycznie uruchomi się jako pierwsza.

Kolejność kończy się następująco: nextTick jest wywołana po zakończeniu bieżącej operacji, rozpoczyna się następna pętla zdarzeń, normalna uruchamiamy fazy pętli zdarzeń, setImmediate wywołujemy i rekurencyjnie wywołujemy naszą funkcję step, aby rozpocząć proces od nowa. Bieżąca operacja kończy się, nextTick pożary, itp.

Wynik powyższego kodu będzie:

nextTick iteration: 0
setImmediate iteration: 0
nextTick iteration: 1
setImmediate iteration: 1
nextTick iteration: 2
setImmediate iteration: 2
nextTick iteration: 3
setImmediate iteration: 3
nextTick iteration: 4
setImmediate iteration: 4
nextTick iteration: 5
setImmediate iteration: 5
nextTick iteration: 6
setImmediate iteration: 6
nextTick iteration: 7
setImmediate iteration: 7
nextTick iteration: 8
setImmediate iteration: 8
nextTick iteration: 9
setImmediate iteration: 9

Teraz przenieśmy nasze wywołanie rekurencyjne do step do naszego nextTick Handlera zamiast setImmediate.

function step(iteration) {
  if (iteration === 10) return;
  setImmediate(() => {
    console.log(`setImmediate iteration: ${iteration}`);
  });
  process.nextTick(() => {
    console.log(`nextTick iteration: ${iteration}`);
    step(iteration + 1); // Recursive call from nextTick handler.
  });
}
step(0);

Teraz, gdy przenieśliśmy wywołanie rekurencyjne do step do nextTick, rzeczy będą zachowywać się w innej kolejności. Nasza pierwsza iteracja pętli zdarzeń uruchamia i wywołuje step rejestrując zarówno obsługę setImmedaite, jak i obsługę nextTick. Po zakończeniu bieżącej operacji wywołany zostanie nextTick handler, który rekurencyjnie wywołuje step i rejestruje inny setImmediate handler, jak również inny nextTick handler. Ponieważ procedura obsługi nextTick zostanie wywołana po bieżącej operacji, zarejestrowanie procedury obsługi nextTick wewnątrz procedury obsługi nextTick spowoduje uruchomienie drugiej procedury obsługi natychmiast po zakończeniu bieżącej operacji obsługi. [[5]} obsługa będzie nadal strzelać, zapobiegając bieżąca pętla zdarzeń z ever continuing. Przejdziemy przez wszystkich naszych opiekunów, zanim zobaczymy pojedynczy ogień.

Wynik powyższego kodu kończy się następująco:

nextTick iteration: 0
nextTick iteration: 1
nextTick iteration: 2
nextTick iteration: 3
nextTick iteration: 4
nextTick iteration: 5
nextTick iteration: 6
nextTick iteration: 7
nextTick iteration: 8
nextTick iteration: 9
setImmediate iteration: 0
setImmediate iteration: 1
setImmediate iteration: 2
setImmediate iteration: 3
setImmediate iteration: 4
setImmediate iteration: 5
setImmediate iteration: 6
setImmediate iteration: 7
setImmediate iteration: 8
setImmediate iteration: 9

Zauważ, że gdybyśmy nie przerwali wywołania rekurencyjnego i nie przerwali go po 10 iteracjach, to wywołania nextTick kontynuowałyby rekurencję i nigdy nie pozwoliłyby pętli zdarzenia przejść do następnej fazy. W ten sposób nextTick może zostać zablokowany, gdy zostanie użyty rekurencyjnie, podczas gdy setImmediate zostanie uruchomiony w następnym zdarzeniu pętla i ustawienie innej procedury obsługi setImmediate z wewnątrz nie spowoduje przerwania bieżącej pętli zdarzenia, pozwalając jej kontynuować wykonywanie faz pętli zdarzenia jak zwykle.

Mam nadzieję, że to pomoże!

PS-zgadzam się z innymi komentatorami, że nazwy dwóch funkcji można łatwo zamienić, ponieważ nextTick brzmi tak, jakby uruchamiała się w następnej pętli zdarzeń, a nie na końcu bieżącej, a koniec pętli bieżącej jest bardziej "natychmiastowy" niż początek następnej pętla. No cóż, to właśnie dostajemy, gdy API dojrzewa i ludzie zaczynają polegać na istniejących interfejsach.

 20
Author: Chev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-31 06:34:01

W prostych słowach, proces.Nexttick () zostanie uruchomiony przy następnym tyknięciu pętli zdarzenia. Jednak setImmediate, zasadniczo ma oddzielną fazę, która zapewnia, że wywołanie zwrotne zarejestrowane w setImmediate() zostanie wywołane dopiero po wywołaniu zwrotnym IO i fazie ankiety.

Proszę odnieść się do tego linku do Nicei Wyjaśnienie: https://medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and-its-metrics-c4907b19da4c

uproszczone zdarzenia pętli zdarzeń

 4
Author: Bharat Raj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-29 10:05:38

Polecam sprawdzić docs sekcję poświęconą Loop, aby lepiej zrozumieć. Jakiś fragment zaczerpnięty stamtąd:

Mamy dwa wywołania, które są podobne, jeśli chodzi o użytkowników, ale ich nazwy są mylące.

  • Proces.nextTick () jest wywołany natychmiast na tej samej fazie

  • SetImmediate() jest wywołane po następującej iteracji lub "klesku"
    pętla zdarzeń

W istocie nazwy powinny być zamieniane. proces.nextTick () uruchamia się bardziej natychmiast niż setImmediate (), ale jest to artefakt z przeszłości, który raczej się nie zmieni.

 0
Author: Valikhan Akhmedov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-21 17:04:38