setImmediate vs. nextTick


336

Версія Node.js 0.10 була випущена сьогодні та представлена setImmediate. Документація, що змінює API, пропонує використовувати її під час здійснення рекурсивних nextTickдзвінків.

З того, що каже MDN, це здається дуже схожим на process.nextTick.

Коли я повинен використовувати nextTickі коли слід використовувати setImmediate?


20
Про цю зміну є 5 абзаців у блозі blog.nodejs.org/2013/03/11/node-v0-10-0-stable
мак

1
З точки зору ефективності це виглядає nextTickшвидше, ніж setImmediateу великих розрахунках.

10
Для запису я прочитав перші п’ять абзаців і все-таки закінчився цим питанням, коли він насправді нічого не зрозумів. Прийнята відповідь набагато більш лаконічна і фактично описує те, що setImmediateробиться більш докладно.
Шев

Я детально пояснив різницю в своєму блозі .
plafer

Це так, що GC може працювати раніше setImmediate, але не раніше nextTick?

Відповіді:


510

Використовуйте, setImmediateякщо ви хочете поставити в чергу чергу за будь-якими зворотними викликами подій вводу-виводу, які вже є у черзі подій. Використовуйте process.nextTickдля ефективної черги функцію на чолі черги подій, щоб вона виконувалася відразу після завершення поточної функції.

Отже, у випадку, коли ви намагаєтеся розбити тривалу роботу, пов’язану з процесором, використовуючи рекурсію, тепер ви хочете використовувати, setImmediateа не process.nextTickв черзі на наступну ітерацію, оскільки в іншому випадку будь-які зворотні виклики подій вводу / виводу не отримають шансу. бігати між ітераціями.


86
Зворотні виклики, передані process.nextTick, зазвичай викликаються в кінці поточного потоку виконання, і, таким чином, приблизно такі ж швидкі, як синхронно викликати функцію. Якщо це не встановлено, це призведе до голодування циклу подій, запобігаючи появі будь-якого вводу-виводу. setImmediates складаються в чергу в створеному порядку і вискакують з черги один раз за ітерацію циклу. Це відрізняється від process.nextTick, який буде виконувати черги зворотних викликів process.maxTickDepth за ітерацію. setImmediate вийде в цикл подій після запуску зворотного дзвінка в чергу, щоб переконатися, що введення-виведення не голодує.
Бенджамін Грюнбаум

2
@UstamanSangat setImmediate підтримується лише IE10 +, всі інші браузери вперто відмовляються реалізовувати ймовірний майбутній стандарт, оскільки їм не подобається, щоб їх били Microsoft. Щоб досягти подібного результату в FF / Chrome, ви можете використовувати postMessage (розмістити повідомлення у власному вікні). Ви також можете скористатися requestAnimationFrame, особливо якщо ваші оновлення пов'язані з інтерфейсом користувача. setTimeout (func, 0) зовсім не працює як process.nextTick.
fabspro

45
@fabspro "тому, що їм не подобається, коли мене б'ють в Microsoft", ви щось болієте. Це здебільшого тому, що це жахливо, жахливо названо. Якщо є один раз setImmediate функція не буде, ніколи не запускається, це негайно. Назва функції - це точно протилежне тому, що вона робить. nextTick і setImmediate краще переключитися; setImmediate виконується відразу після завершення поточного стека (до очікування вводу / виводу), а nextTick виконує в кінці наступного галочки (після очікування вводу / виводу). Але тоді це вже було сказано тисячу разів.
Крейг Ендрюс

4
@fabspro Але, на жаль, функція називається nextTick. nextTick виконує "негайно", тоді як setImmediate більше схожий на setTimeout / postMessage.
Роберт

1
@CraigAndrews я б уникнув, requestAnimationFrameтому що це не завжди відбувається (я напевно це бачив, я думаю, що прикладом була вкладка не була поточною вкладкою), і її можна буде викликати до того, як сторінка завершить малювання (тобто браузер все ще зайнятий малюванням).
робот

68

Як ілюстрація

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);
  });
});

дасть наступний вихід

Start
Done
NT1
NT2
NT3
TO1
IO2
IO3
IM1
IM2
IM3
IM4
IO1

Я сподіваюся, що це може допомогти зрозуміти різницю.

Оновлено:

process.nextTick()Відкликані виклики, відкладені з запуском до запуску будь-якої іншої події вводу-виводу, тоді як при setImmediate (), виконання ставиться в чергу за будь-яким подією вводу-виводу, що вже знаходиться в черзі.

Шаблони дизайну Node.js від Маріо Касчаро (мабуть, найкраща книга про node.js / js)


2
Це дуже корисно дякую. Я думаю, що образи та приклади - найшвидший спосіб зрозуміти щось.
Джон Джеймс

1
Я думаю, що важливо зазначити, що setTimeout () та setImmediate (), коли не входять до циклу вводу / виводу, порядок є недетермінованим залежно від продуктивності процесу. nodejs.org/en/docs/guides/event-loop-timers-and-nexttick For example, if we run the following script which is not within an I/O cycle (i.e. the main module), the order in which the two timers are executed is non-deterministic, as it is bound by the performance of the process: Отже, ця відповідь насправді не відповідає точній різниці, а лише приклад, який може змінюватися в різних умовах
Actung

Як вказував @Actung. Дуже важливо знати, чи встановлені setTimetout та setImmediate в циклі вводу-виводу чи ні, щоб визначити результат.
Rajika Imal

50

Я думаю, що можу це досить добре проілюструвати. Оскільки nextTickвикликається в кінці поточної операції, виклик її рекурсивно може призвести до блокування продовження циклу подій. setImmediateвирішує це шляхом запуску на етапі перевірки циклу подій, дозволяючи циклу подій тривати нормально.

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

джерело: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

Зауважте, що фаза перевірки знаходиться відразу після фази опитування. Це пояснюється тим, що фаза опитування та зворотні виклики вводу / виводу є найбільш імовірними місцями, на setImmediateякі збираються виконувати ваші дзвінки . Тому в ідеалі більшість цих дзвінків насправді будуть досить негайними, не такими ж негайними, як nextTickперевіряється після кожної операції та технічно існує поза циклом подій.

Давайте розглянемо невеликий приклад різниці між setImmediateі 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);

Скажімо, ми щойно запустили цю програму і проходимо першу ітерацію циклу подій. Він викличе stepфункцію з нулем ітерації. Потім він зареєструє два обробники, один для setImmediateта один для process.nextTick. Потім рекурсивно викликаємо цю функцію від setImmediateобробника, який буде виконуватися на наступній фазі перевірки. nextTickОброблювач буде працювати в кінці поточного операції переривання циклу обробки подій, так що навіть якщо він був зареєстрований другим буде реально працювати першим.

Порядок закінчується таким чином: nextTickспрацьовує, коли закінчується поточна операція, починається наступний цикл подій, виконуються нормальні фази циклу подій, setImmediateспрацьовує і рекурсивно викликає нашу stepфункцію, щоб запустити процес заново. Поточні операції закінчуються, nextTickпожежі тощо.

Вихід з вищевказаного коду буде:

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

Тепер перенесемо наш рекурсивний виклик stepв наш nextTickобробник замість 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);

Тепер, коли ми перемістили рекурсивний виклик stepв nextTickобробник, речі будуть вести себе в іншому порядку. Наша перша ітерація циклу подій працює і вимагає stepреєстрації setImmedaiteобробника, а також nextTickобробника. Після завершення поточної операції наш nextTickобробник спрацьовує, який рекурсивно викликає stepта реєструє іншого setImmediateобробника, а також іншого nextTickобробника. Оскільки nextTickобробник спрацьовує після поточної операції, реєстрація nextTickобробника в nextTickобробці призведе до запуску другого обробника відразу після завершення поточної операції обробника. Ці nextTickобробники будуть продовжувати стріляти, запобігаючи поточний цикл подій з коли - або продовження. Ми проберемося через усі нашіnextTickобробники, перш ніж ми побачимо єдиний setImmediateобстріл обробника.

Вихід з наведеного коду закінчується таким:

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

Зауважте, якби ми не переривали рекурсивний виклик і переривали його після 10 ітерацій, то nextTickдзвінки продовжувались повторюватися і ніколи не дозволяти циклу подій продовжувати переходити до наступної фази. Це nextTickможе стати блокуванням при рекурсивному використанні, тоді як setImmediateв наступному циклі події буде запущено, а встановлення іншого setImmediateобробника зсередини не перериватиме поточний цикл подій взагалі, дозволяючи йому продовжувати виконувати фази циклу подій як нормально.

Сподіваюся, що це допомагає!

PS - Я погоджуюся з іншими коментаторами, що імена двох функцій можна легко поміняти, оскільки nextTickзвучить так, що це буде спрацьовувати в наступному циклі подій, а не в кінці поточного, а кінець поточного циклу є більш "негайним" ", ніж початок наступного циклу. Ну добре, це те, що ми отримуємо, коли API дозріває, і люди залежать від існуючих інтерфейсів.


2
Досить чіткий опис. Я думаю, що ця відповідь потребує більшої кількості результатів.
Actung

Добре пояснено (Y)
Dhiraj Sharma

важливо повторити попередження Node про використання process.nextTick. Якщо в наступномуTickQueue виклик великої кількості зворотних викликів, ви можете потенційно голодувати циклом подій, гарантуючи, що фаза опитування ніколи не буде досягнута. Це причина, чому ви, як правило, віддаєте перевагу setImmediate.
faridcs

1
Дякую, це було найкращим поясненням. зразок коду справді допоміг.
skyhavoc

@skyhavoc радий, що можу допомогти!
Шев

30

У коментарях у відповіді прямо не зазначено, що nextTick перейшов з макросемантики в мікросемантику.

перед вузлом 0,9 (коли було введено setImmediate), наступнийTick діяв на початку наступного стовпчика виклику.

оскільки вузол 0,9, nextTick працює в кінці існуючого стовпчика виклику, тоді як setImmediate знаходиться на початку наступного стовпчика виклику

перегляньте https://github.com/YuzuJS/setImmediate для інструментів та деталей


11

Простіше кажучи, process.NextTick () виконується при наступному галочці циклу подій. Однак, setImmediate, в основному, має окрему фазу, яка гарантує, що зворотний виклик, зареєстрований під setImmediate (), буде викликаний лише після фази зворотного виклику IO та фази опитування.

Перейдіть за цим посиланням для приємного пояснення: https://medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and -its-metrics-c4907b19da4c

спрощена подія циклу подій


8

Тут є кілька чудових відповідей, де детально описано, як вони працюють.

Просто додайте відповідь на конкретне запитання:

Коли я повинен використовувати nextTickі коли слід використовувати setImmediate?


Завжди використовуйте setImmediate.


Цикл подій, таймери таprocess.nextTick() doc Node.js включає в себе наступне:

Ми рекомендуємо розробникам використовувати setImmediate()в усіх випадках, тому що простіше міркувати (а це призводить до коду, сумісного з більш широким спектром середовищ, як-от браузер JS.)


Раніше в документі він попереджав, що process.nextTickможе призвести до ...

деякі погані ситуації, оскільки це дозволяє "голодувати" введення / виводу, здійснюючи рекурсивні process.nextTick()дзвінки , що не дозволяє циклу подій досягти фази опитування .

Як з'ясовується, process.nextTick можна навіть голодувати Promises:

Promise.resolve().then(() => { console.log('this happens LAST'); });

process.nextTick(() => {
  console.log('all of these...');
  process.nextTick(() => {
    console.log('...happen before...');
    process.nextTick(() => {
      console.log('...the Promise ever...');
      process.nextTick(() => {
        console.log('...has a chance to resolve');
      })
    })
  })
})

З іншого боку, setImmediate" простіше міркувати " і уникає таких питань:

Promise.resolve().then(() => { console.log('this happens FIRST'); });

setImmediate(() => {
  console.log('this happens LAST');
})

Тому, якщо немає конкретної потреби в унікальній поведінці process.nextTick, рекомендований підхід полягає в тому, щоб " використовувати setImmediate()у всіх випадках ".


1

Я рекомендую вам перевірити документиДля кращого розуміння розділ " ", присвячений Loop. Деякі фрагменти, взяті звідти:

У нас є два дзвінки, схожі на користувачів, але їхні назви заплутані.

  • process.nextTick () запускається негайно на одній фазі

  • setImmediate () спрацьовує при наступній ітерації або "галочці"
    циклу подій

По суті, імена слід замінювати. process.nextTick () запускається швидше, ніж setImmediate (), але це артефакт минулого, який навряд чи зміниться.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.