setImmediate vs nextTick

336

Node.js versi 0.10 dirilis hari ini dan diperkenalkan setImmediate. The perubahan API dokumentasi menyarankan menggunakannya ketika melakukan rekursif nextTickpanggilan.

Dari apa yang dikatakan MDN tampaknya sangat mirip process.nextTick.

Kapan saya harus menggunakan nextTickdan kapan saya harus menggunakan setImmediate?

Benjamin Gruenbaum
sumber
20
Ada 5 paragraf tentang perubahan ini di blog blog.nodejs.org/2013/03/11/node-v0-10-0-stable
mak
1
Dari tolok ukur kinerja, sepertinya nextTicklebih cepat daripada setImmediateperhitungan besar.
10
Sebagai catatan, saya membaca lima paragraf pertama dan masih berakhir pada pertanyaan ini ketika tidak benar-benar menjelaskan apa pun bagi saya. Jawaban yang diterima jauh lebih singkat dan sebenarnya menggambarkan apa yang setImmediatedilakukan dengan lebih baik.
Chev
Saya telah menjelaskan perbedaannya dengan sangat rinci di blog saya .
plafer
Apakah ini kasus bahwa GC dapat berjalan sebelumnya setImmediate, tetapi tidak sebelumnya nextTick?

Jawaban:

511

Gunakan setImmediatejika Anda ingin mengantri fungsi di balik callback acara I / O apa pun yang sudah ada dalam antrian acara. Gunakan process.nextTickuntuk secara efektif mengantri fungsi di kepala antrian acara sehingga dieksekusi segera setelah fungsi saat ini selesai.

Jadi dalam kasus di mana Anda mencoba untuk memecah pekerjaan yang berjalan lama, terikat dengan CPU menggunakan rekursi, Anda sekarang ingin menggunakan setImmediatedaripada process.nextTickuntuk mengantri pada iterasi berikutnya karena jika tidak, setiap callback acara I / O tidak akan mendapatkan kesempatan. untuk menjalankan antar iterasi.

JohnnyHK
sumber
86
Callback diteruskan ke process.nextTick biasanya akan dipanggil pada akhir arus eksekusi, dan dengan demikian kira-kira sama cepatnya dengan memanggil fungsi secara sinkron. Jika tidak dicentang, ini akan membuat loop peristiwa kelaparan, mencegah I / O apa pun terjadi. setImmediates di-antri dalam urutan yang dibuat, dan dikeluarkan dari antrian sekali per loop perulangan. Ini berbeda dari process.nextTick yang akan mengeksekusi callback queue process.maxTickDepth per iterasi. setImmediate akan menghasilkan ke loop acara setelah memecat panggilan balik antrian untuk memastikan I / O tidak sedang kelaparan.
Benjamin Gruenbaum
2
@UstamanSangat setImmediate hanya didukung oleh IE10 +, semua browser lainnya dengan keras kepala menolak untuk menerapkan standar yang mungkin terjadi di masa depan karena mereka tidak suka dikalahkan oleh Microsoft. Untuk mencapai hasil yang serupa di FF / Chrome, Anda dapat menggunakan postMessage (memposting pesan ke jendela Anda sendiri). Anda dapat mempertimbangkan menggunakan requestAnimationFrame juga, terutama jika pembaruan Anda terkait UI. setTimeout (func, 0) tidak berfungsi seperti process.nextTick sama sekali.
fabspro
45
@fabspro "karena mereka tidak suka dipukuli dengan Microsoft saya" membuat Anda terdengar kesal tentang sesuatu. Itu sebagian besar karena itu sangat, sangat bernama. Jika ada satu fungsi setImmediate tidak akan pernah berjalan, itu segera. Nama fungsi adalah kebalikan dari apa yang dilakukannya. nextTick dan setImmediate akan lebih baik beralih; setImmediate dieksekusi segera setelah tumpukan saat ini selesai (sebelum menunggu I / O) dan nextTick dijalankan pada akhir centang berikutnya (setelah menunggu I / O). Tapi kemudian, ini sudah dikatakan ribuan kali.
Craig Andrews
4
@fabspro Namun sayangnya fungsinya disebut nextTick. nextTick mengeksekusi "segera" sementara setImmediate lebih seperti setTimeout / postMessage.
Robert
1
@CraigAndrews saya akan menghindari requestAnimationFramekarena itu tidak selalu terjadi (saya sudah pasti melihat ini, saya pikir contoh adalah tab bukan tab saat ini) dan dapat dipanggil sebelum halaman selesai melukis (yaitu browser masih sibuk menggambar).
robocat
68

Sebagai ilustrasi

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

akan memberikan output berikut

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

Saya harap ini dapat membantu untuk memahami perbedaannya.

Diperbarui:

Panggilan balik ditunda dengan process.nextTick()dijalankan sebelum acara I / O lainnya dipecat, sementara dengan setImmediate (), eksekusi akan antri di belakang setiap peristiwa I / O yang sudah ada dalam antrian.

Node.js Design Patterns , oleh Mario Casciaro (mungkin buku terbaik tentang node.js / js)

Dragan
sumber
2
Ini sangat membantu, terima kasih. Saya pikir gambar dan contoh adalah cara tercepat untuk memahami sesuatu.
John James
1
Saya pikir sangat penting untuk menunjukkan bahwa setTimeout () dan setImmediate () ketika tidak dalam siklus I / O urutannya adalah non-deterministik tergantung pada kinerja suatu proses. 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: Jadi, jawaban ini tidak benar-benar menjawab perbedaan persis tetapi hanya sebuah contoh yang dapat bervariasi dalam konteks yang berbeda
Actung
Seperti yang ditunjukkan oleh @Actung. sangat penting untuk mengetahui apakah setTimetout dan setImmediate berada dalam siklus I / O atau tidak, untuk menentukan hasilnya.
Rajika Imal
50

Saya pikir saya bisa menggambarkan ini dengan cukup baik. Karena nextTickdipanggil pada akhir operasi saat ini, memanggilnya secara rekursif dapat berakhir memblokir perulangan peristiwa dari melanjutkan. setImmediatemenyelesaikan ini dengan menembakkan pada fase pemeriksaan loop acara, yang memungkinkan loop acara berlanjut secara normal.

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

sumber: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

Perhatikan bahwa fase pemeriksaan segera setelah fase polling. Ini karena fase polling dan I / O callback adalah tempat yang paling mungkin setImmediateuntuk menjalankan panggilan Anda . Jadi idealnya sebagian besar panggilan itu sebenarnya akan sangat segera, hanya saja tidak secepat nextTickyang diperiksa setelah setiap operasi dan secara teknis ada di luar loop acara.

Mari kita lihat sedikit contoh perbedaan antara setImmediatedan 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);

Katakanlah kita baru saja menjalankan program ini dan sedang melangkah melalui iterasi pertama dari loop acara. Ini akan memanggil stepfungsi dengan iterasi nol. Kemudian akan mendaftarkan dua penangan, satu untuk setImmediatedan satu untuk process.nextTick. Kami kemudian secara rekursif memanggil fungsi ini dari setImmediatehandler yang akan dijalankan pada tahap pemeriksaan berikutnya. The nextTickhandler akan berjalan pada akhir operasi saat ini mengganggu loop acara, jadi meskipun itu telah didaftarkan kedua itu benar-benar akan berjalan pertama.

Urutan akhirnya adalah: nextTickkebakaran saat operasi saat ini berakhir, loop acara berikutnya dimulai, fase loop peristiwa normal dieksekusi, setImmediatediaktifkan dan secara rekursif memanggil stepfungsi kami untuk memulai proses dari awal lagi. Operasi saat ini berakhir, nextTickkebakaran, dll.

Output dari kode di atas adalah:

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

Sekarang mari kita pindahkan panggilan rekursif kita ke stepdalam nextTickhandler kita alih-alih 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);

Sekarang kami telah memindahkan panggilan rekursif ke stepdalam nextTickhandler hal-hal akan berperilaku dalam urutan yang berbeda. Iterasi pertama kami dari loop acara berjalan dan panggilan stepmendaftarkan setImmedaitepenangan serta nextTickpenangan. Setelah operasi saat ini berakhir, nextTickpenangan kami akan melakukan panggilan secara rekursif stepdan mendaftarkan setImmediatepenangan yang lain serta nextTickpenangan yang lain . Karena nextTickpawang menyala setelah operasi saat ini, mendaftarkan nextTickpawang dalam nextTickpawang akan menyebabkan pawang kedua berjalan segera setelah operasi pawang saat ini selesai. The nextTickpenangan akan terus menembak, mencegah event loop arus dari yang pernah melanjutkan. Kami akan melewati semua kaminextTickpenangan sebelum kita melihat setImmediateapi penangan tunggal .

Hasil akhir dari kode di atas adalah:

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

Perhatikan bahwa jika kita tidak menginterupsi panggilan rekursif dan membatalkannya setelah 10 iterasi maka nextTickpanggilan akan terus berulang dan tidak pernah membiarkan loop acara berlanjut ke fase berikutnya. Ini adalah bagaimana nextTickbisa menjadi pemblokiran ketika digunakan secara rekursif sedangkan setImmediateakan menyala di loop acara berikutnya dan pengaturan setImmediatehandler lain dari dalam satu tidak akan mengganggu loop acara saat ini sama sekali, yang memungkinkannya untuk melanjutkan menjalankan fase-fase dari loop acara seperti biasa.

Semoga itu bisa membantu!

PS - Saya setuju dengan komentator lain bahwa nama kedua fungsi dapat dengan mudah ditukar karena nextTickterdengar seperti itu akan menyala di loop acara berikutnya daripada akhir yang sekarang, dan akhir dari loop saat ini lebih "segera "Dari awal loop berikutnya. Oh well, itulah yang kami dapatkan ketika API matang dan orang-orang bergantung pada antarmuka yang ada.

Chev
sumber
2
Deskripsi yang cukup jelas. Saya pikir jawaban ini perlu lebih banyak upvotes.
Actung
Diterangkan dengan baik (Y)
Dhiraj Sharma
penting untuk mengulangi peringatan Node tentang penggunaan process.nextTick. Jika Anda meminta sejumlah besar panggilan balik di nextTickQueue, Anda dapat berpotensi kelaparan loop acara dengan memastikan fase polling tidak pernah tercapai. Inilah alasan mengapa Anda umumnya harus memilih setImmediate.
faridcs
1
Terima kasih, ini penjelasan terbaik. kode sampel sangat membantu.
skyhavoc
@skyhavoc senang saya bisa membantu!
Chev
30

Dalam komentar di jawaban, itu tidak secara eksplisit menyatakan bahwa nextTick bergeser dari Macrosemantics ke Microsemantics.

sebelum node 0.9 (ketika setImmediate diperkenalkan), nextTick dioperasikan pada awal callstack berikutnya.

sejak node 0.9, nextTick beroperasi di akhir dari callstack yang ada, sedangkan setImmediate adalah pada awal dari callstack berikutnya

lihat https://github.com/YuzuJS/setImmediate untuk alat dan detail

Jay Day Zee
sumber
11

Dalam istilah sederhana, process.NextTick () akan dieksekusi pada tick berikutnya dari loop acara. Namun, setImmediate, pada dasarnya memiliki fase terpisah yang memastikan bahwa callback yang terdaftar di bawah setImmediate () akan dipanggil hanya setelah fase callback dan polling IO.

Silakan merujuk ke tautan ini untuk penjelasan yang bagus: https://medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and -its-metrics-c4907b19da4c

event loop acara yang disederhanakan

Bir jahe
sumber
8

Beberapa jawaban bagus di sini merinci bagaimana keduanya bekerja.

Hanya menambahkan satu yang menjawab pertanyaan spesifik yang diajukan:

Kapan saya harus menggunakan nextTickdan kapan saya harus menggunakan setImmediate?


Selalu gunakan setImmediate.


The Node.js Acara Loop Timers, danprocess.nextTick() doc meliputi berikut ini:

Kami menyarankan pengembang menggunakan setImmediate()dalam semua kasus karena lebih mudah untuk dipertimbangkan (dan itu mengarah ke kode yang kompatibel dengan berbagai lingkungan yang lebih luas, seperti browser JS.)


Sebelumnya dalam dokumen itu diperingatkan bahwa process.nextTick...

beberapa situasi buruk karena memungkinkan Anda untuk "kelaparan" I / O Anda dengan membuat process.nextTick()panggilan rekursif , yang mencegah loop peristiwa dari mencapai fase polling .

Ternyata, process.nextTickbahkan bisa kelaparan 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');
      })
    })
  })
})

Di sisi lain, setImmediate" lebih mudah untuk dipertimbangkan " dan menghindari jenis masalah ini:

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

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

Jadi kecuali ada kebutuhan khusus untuk perilaku unik process.nextTick, pendekatan yang disarankan adalah " digunakan setImmediate()dalam semua kasus ".

Brian Adams
sumber
1

Saya sarankan Anda untuk memeriksa bagian dokumen yang didedikasikan untuk Loop untuk mendapatkan pemahaman yang lebih baik. Beberapa cuplikan diambil dari sana:

Kami memiliki dua panggilan yang sama sejauh menyangkut pengguna, tetapi nama mereka membingungkan.

  • process.nextTick () langsung aktif pada fase yang sama

  • setImmediate () diaktifkan pada iterasi berikut atau 'centang' dari
    loop acara

Intinya, nama harus ditukar. process.nextTick () dijalankan lebih cepat daripada setImmediate (), tetapi ini adalah artefak dari masa lalu yang tidak mungkin berubah.

Valikhan Akhmedov
sumber