ฉันคิดว่าฉันสามารถอธิบายเรื่องนี้ได้เป็นอย่างดี เนื่องจาก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/
ขอให้สังเกตว่าขั้นตอนการตรวจสอบทันทีหลังจากขั้นตอนการสำรวจความคิดเห็น เนื่องจากขั้นตอนการสำรวจความคิดเห็นและการโทรกลับ I / O เป็นสถานที่ที่มีแนวโน้มที่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
ตอนนี้ขอย้ายโทร recursive ของเราที่จะstepเข้ามาเราจัดการแทนnextTicksetImmediate
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);
ตอนนี้เราได้ย้ายโทร recursive 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จัดการอื่นจากภายในหนึ่งจะไม่ขัดขวางวงเหตุการณ์ปัจจุบันที่ทุกคนอนุญาตให้ดำเนินการขั้นตอนของการวนเหตุการณ์ต่อไปตามปกติ
หวังว่าจะช่วย!
ป.ล. - ฉันเห็นด้วยกับผู้วิจารณ์คนอื่น ๆ ว่าชื่อของทั้งสองฟังก์ชั่นนั้นสามารถสลับได้อย่างง่ายดายเนื่องจากnextTickฟังดูเหมือนว่าจะเกิดขึ้นในลูปเหตุการณ์ถัดไปแทนที่จะจบตอนปัจจุบันและท้ายของลูปปัจจุบันนั้น "มากกว่า "กว่าจุดเริ่มต้นของลูปถัดไป โอ้นั่นคือสิ่งที่เราได้รับเมื่อ API เติบโตขึ้นและผู้คนต่างก็พึ่งพาอินเทอร์เฟซที่มีอยู่