ฉันคิดว่าฉันสามารถอธิบายเรื่องนี้ได้เป็นอย่างดี เนื่องจาก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
เข้ามาเราจัดการแทน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);
ตอนนี้เราได้ย้ายโทร 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 เติบโตขึ้นและผู้คนต่างก็พึ่งพาอินเทอร์เฟซที่มีอยู่