setImmediate vs. nextTick


336

รุ่น Node.js 0.10 setImmediateได้รับการปล่อยตัวในวันนี้และแนะนำ เปลี่ยนแปลง APIเอกสารแนะนำให้ใช้มันเมื่อทำ recursive nextTickโทร

จากสิ่งที่MDN กล่าวว่าprocess.nextTickดูเหมือนว่าคล้ายกับ

ฉันควรใช้nextTickเมื่อใดและควรใช้เมื่อsetImmediateใด


20
มี 5 ย่อหน้าเกี่ยวกับการเปลี่ยนแปลงนี้ในบล็อกblog.nodejs.org/2013/03/11/node-v0-10-0-stable
mak

1
จากการวัดประสิทธิภาพดูเหมือนว่าnextTickจะเร็วกว่าการsetImmediateคำนวณขนาดใหญ่

10
สำหรับบันทึกฉันอ่านห้าย่อหน้าแรกและยังคงจบลงที่คำถามนี้เมื่อมันไม่ได้ล้างอะไรเลยสำหรับฉัน คำตอบที่ได้รับการยอมรับมีความรัดกุมมากกว่าและอธิบายสิ่งที่setImmediateทำในรายละเอียดได้ดีกว่า
Chev

ผมได้อธิบายความแตกต่างในรายละเอียดมากในของฉันบล็อก
วางมือ

เป็นกรณีที่ GC สามารถเรียกใช้ก่อนหน้าsetImmediateนี้nextTickหรือไม่

คำตอบ:


510

ใช้setImmediateหากคุณต้องการคิวฟังก์ชั่นที่อยู่เบื้องหลังสิ่งที่ I / O การเรียกกลับเหตุการณ์ที่อยู่ในคิวเหตุการณ์ ใช้process.nextTickเพื่อจัดคิวฟังก์ชันที่ส่วนหัวของคิวเหตุการณ์อย่างมีประสิทธิภาพเพื่อให้สามารถดำเนินการได้ทันทีหลังจากที่ฟังก์ชันปัจจุบันเสร็จสมบูรณ์

ดังนั้นในกรณีที่คุณพยายามที่จะเลิกงานที่ใช้เวลานาน, CPU-bound โดยใช้การเรียกซ้ำ, ตอนนี้คุณต้องการที่จะใช้setImmediateมากกว่าที่process.nextTickจะจัดคิวการทำซ้ำครั้งต่อไปมิฉะนั้นการเรียกเหตุการณ์ I / O จะไม่ได้รับโอกาส เพื่อรันระหว่างการวนซ้ำ


86
การเรียกกลับที่ส่งผ่านไปยัง process.nextTick มักจะถูกเรียกเมื่อสิ้นสุดการไหลของการดำเนินการในปัจจุบันและดังนั้นจึงมีความรวดเร็วเท่ากับการเรียกใช้ฟังก์ชันพร้อมกัน ไม่ได้ตรวจสอบสิ่งนี้จะทำให้ลูปของเหตุการณ์เกิดการวนซ้ำทำให้ I / O ใด ๆ เกิดขึ้น setImmediates ถูกจัดคิวตามลำดับที่สร้างขึ้นและถูกดึงออกจากคิวหนึ่งครั้งต่อการวนซ้ำลูป สิ่งนี้แตกต่างจาก process.nextTick ซึ่งจะเรียกใช้งาน process.maxTickDepth จัดคิวการโทรกลับต่อการทำซ้ำ setImmediate จะให้ผลกับลูปเหตุการณ์หลังจากทำการโทรกลับที่อยู่ในคิวเพื่อให้แน่ใจว่า I / O ไม่ได้รับการอดอาหาร
Benjamin Gruenbaum

2
@UstamanSangat setImmediate ได้รับการสนับสนุนโดย IE10 + เท่านั้นเบราว์เซอร์อื่น ๆ ทั้งหมดปฏิเสธที่จะใช้มาตรฐานในอนาคตอย่างดื้อรั้นเพราะพวกเขาไม่ชอบที่จะถูก Microsoft โจมตี เพื่อให้ได้ผลลัพธ์ที่คล้ายกันใน FF / Chrome คุณสามารถใช้ postMessage (โพสต์ข้อความไปที่หน้าต่างของคุณเอง) คุณสามารถพิจารณาใช้ requestAnimationFrame ได้เช่นกันโดยเฉพาะอย่างยิ่งหากการอัปเดตของคุณเกี่ยวข้องกับ UI setTimeout (func, 0) ไม่ทำงานเหมือน process.nextTick เลย
fabspro

45
@ fabspro "เพราะพวกเขาไม่ชอบที่จะถูกตี Microsoft ของฉัน" ทำให้คุณฟังเกี่ยวกับบางสิ่งบางอย่าง ส่วนใหญ่เป็นเพราะมันชื่อชะมัด หากมีฟังก์ชั่น setImmediate เพียงครั้งเดียวจะไม่ทำงานก็จะทำงานทันที ชื่อของฟังก์ชั่นนั้นตรงกันข้ามกับสิ่งที่มันทำ ถัดไปคลิกและตั้งค่าการดีกว่าจะสลับไปมาได้ดีกว่า setImmediate ดำเนินการทันทีหลังจากที่สแต็คปัจจุบันเสร็จสมบูรณ์ (ก่อนรอ I / O) และ nextTick ดำเนินการที่จุดสิ้นสุดของเห็บถัดไป (หลังจากรอ I / O) ทว่าสิ่งนี้มีคนพูดกันพันครั้งแล้ว
Craig Andrews

4
@ fabspro แต่น่าเสียดายที่ฟังก์ชั่นนี้มีชื่อว่า nextTick nextTick ดำเนินการ "ทันที" ในขณะที่ setImmediate เป็นเหมือน setTimeout / postMessage
Robert

1
@ CraigAndrews ฉันจะหลีกเลี่ยงrequestAnimationFrameเพราะมันไม่ได้เกิดขึ้นเสมอ (ฉันได้เห็นอย่างนี้แล้วฉันคิดว่าตัวอย่างคือแท็บไม่ใช่แท็บปัจจุบัน) และสามารถเรียกได้ก่อนที่หน้าจะเสร็จสิ้นการวาดภาพ (เช่นเบราว์เซอร์ยังคงยุ่งอยู่)
robocat

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

ฉันหวังว่านี่จะช่วยให้เข้าใจความแตกต่าง

Updated:

การprocess.nextTick()เรียกกลับที่เลื่อนเวลาด้วยการเรียกใช้ก่อนที่เหตุการณ์ I / O อื่น ๆ จะเริ่มทำงานในขณะที่ setImmediate () การดำเนินการจะถูกจัดคิวไว้หลังเหตุการณ์ I / O ใด ๆ ที่อยู่ในคิวแล้ว

รูปแบบการออกแบบ Node.jsโดย Mario Casciaro (อาจเป็นหนังสือที่ดีที่สุดเกี่ยวกับ node.js / js)


2
ขอบคุณจริง ๆ ที่เป็นประโยชน์ ฉันคิดว่ารูปภาพและตัวอย่างเป็นวิธีที่เร็วที่สุดในการเข้าใจบางสิ่งบางอย่าง
John James

1
ฉันคิดว่ามันเป็นสิ่งสำคัญที่จะต้องชี้ให้เห็นว่า setTimeout () และ setImmediate () เมื่อไม่อยู่ในวัฏจักร I / O คำสั่งนั้นไม่ได้กำหนดขึ้นอยู่กับประสิทธิภาพของกระบวนการ 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 อยู่ในวัฏจักร I / O หรือไม่เพื่อพิจารณาผลลัพธ์
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/

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


2
คำอธิบายที่ชัดเจนสวย ฉันคิดว่าคำตอบนี้ต้องการผู้โหวตมากขึ้น
Actung

อธิบายได้ดี (Y)
Dhiraj Sharma

สิ่งสำคัญคือต้องย้ำคำเตือนของโหนดเกี่ยวกับการใช้ process.nextTick หากคุณทำการโทรกลับจำนวนมากใน NextTickQueue ครั้งต่อไปคุณสามารถอดอาหารวนเหตุการณ์โดยมั่นใจว่าขั้นตอนการสำรวจจะไม่ถึง นี่คือเหตุผลที่คุณควรเลือก setImmediate
Faridcs

1
ขอบคุณนี่เป็นคำอธิบายที่ดีที่สุด รหัสตัวอย่างช่วยได้จริงๆ
skyhavoc

@skyhavoc ดีใจที่ฉันช่วยได้!
Chev

30

ในความคิดเห็นในคำตอบนั้นไม่ได้ระบุอย่างชัดเจนว่าถัดไปเปลี่ยนจาก Macrosemantics เป็น Microsemantics

ก่อนที่โหนด 0.9 (เมื่อมีการแนะนำ setImmediate) nextTick จะดำเนินการเมื่อเริ่มต้น callstack ถัดไป

ตั้งแต่โหนด 0.9 nextTick ทำงานที่ส่วนท้ายของ callstack ที่มีอยู่ในขณะที่ setImmediate อยู่ที่จุดเริ่มต้นของ callstack ถัดไป

ตรวจสอบ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-เมตริก c4907b19da4c

ลดความซับซ้อนของเหตุการณ์ห่วงเหตุการณ์


8

บางคำตอบที่ดีที่นี่มีรายละเอียดวิธีการทำงาน

เพียงเพิ่มที่ตอบคำถามที่ถาม:

ฉันควรใช้nextTickเมื่อใดและควรใช้เมื่อsetImmediateใด


setImmediateการใช้งานเสมอ


Node.js เหตุการณ์วงจับเวลาและprocess.nextTick()เอกสารรวมถึงต่อไปนี้:

เราขอแนะนำให้นักพัฒนาซอฟต์แวร์ใช้setImmediate()ในทุกกรณีเพราะง่ายต่อการให้เหตุผล (และนำไปสู่รหัสที่เข้ากันได้กับสภาพแวดล้อมที่หลากหลายเช่นเบราว์เซอร์ JS)


ก่อนหน้านี้ในเอกสารมันเตือนว่าprocess.nextTickสามารถนำไปสู่ ​​...

บางสถานการณ์ที่ไม่ดีเนื่องจากช่วยให้คุณ "อดอยาก" I / O ของคุณด้วยการ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

ฉันแนะนำให้คุณตรวจสอบส่วนเอกสารเฉพาะสำหรับลูปเพื่อทำความเข้าใจให้ดีขึ้น ตัวอย่างบางส่วนนำมาจากที่นั่น:

เรามีการโทรสองครั้งที่คล้ายกันกับที่ผู้ใช้มีความกังวล แต่ชื่อของพวกเขาสับสน

  • process.nextTick () ยิงทันทีในเฟสเดียวกัน

  • setImmediate () fires ในการวนซ้ำต่อไปนี้หรือ 'ขีด' ของการ
    วนรอบเหตุการณ์

โดยพื้นฐานแล้วชื่อควรถูกเปลี่ยน process.nextTick () ยิงได้เร็วกว่า setImmediate () แต่นี่เป็นสิ่งประดิษฐ์ในอดีตซึ่งไม่น่าจะเปลี่ยนแปลงได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.