libuv के फेज़, setImmediate और setTimeout के बीच का असली अंतर, और अवरुद्ध इवेंट लूप का पता कैसे लगाएँ, यह जानें।
इस पाठ को Kodokon में खोलेंNode.js अपने इवेंट लूप को libuv को सौंपता है, जो इसे अलग-अलग फेज़ में व्यवस्थित करता है: timers (setTimeout, setInterval), pending callbacks, idle/prepare (आंतरिक), poll (I/O: सॉकेट, फ़ाइलें), check (setImmediate), और close callbacks। हर टिक पर, लूप इन फेज़ से इसी क्रम में गुज़रता है। एक ऐसी बारीकी जिसे कम ही डेवलपर जानते हैं: हर कॉलबैक के बीच, Node पहले process.nextTick कतार को खाली करता है, फिर प्रॉमिस माइक्रोटास्क को - और Node 11 से ऐसा ही है, यानी हर कॉलबैक के बाद, न कि केवल फेज़ के बीच।
import { readFile } from "node:fs";
setTimeout(() => console.log("timeout"), 0);
setImmediate(() => console.log("immediate"));
readFile(new URL(import.meta.url), () => {
setTimeout(() => console.log("io timeout"), 0);
setImmediate(() => console.log("io immediate"));
});जब स्क्रिप्ट शुरू होती है, तब setTimeout(fn, 0) और setImmediate(fn) के बीच का क्रम अनिश्चित होता है: setTimeout(fn, 0) वास्तव में 1 ms तक सीमित कर दिया जाता है, और यदि लूप एक मिलीसेकंड से कम में timers फेज़ तक पहुँच जाता है, तो टाइमर अभी योग्य नहीं होता। लेकिन किसी I/O कॉलबैक के अंदर, आप poll फेज़ में होते हैं: check फेज़ (setImmediate) हमेशा timers फेज़ पर लौटने से पहले आता है। इसलिए io immediate लगातार io timeout से पहले प्रिंट होता है।
setTimeout(() => {
process.nextTick(() => console.log("nextTick"));
Promise.resolve().then(() => console.log("promise"));
queueMicrotask(() => console.log("microtask"));
console.log("sync");
}, 0);
// sync, nextTick, promise, microtaskएक सूक्ष्म जाल: किसी ES मॉड्यूल के शीर्ष स्तर पर, यह क्रम उलट जाता है - प्रॉमिस माइक्रोटास्क process.nextTick से पहले चलते हैं। इसका कारण: किसी ES मॉड्यूल का मूल्यांकन स्वयं एक प्रॉमिस जॉब के रूप में निष्पादित होता है, इसलिए जब आपका सिंक्रोनस कोड समाप्त होता है, तब तक माइक्रोटास्क कतार पहले से ही खाली की जा रही होती है। CommonJS में, nextTick शीर्ष स्तर पर भी अपनी प्राथमिकता बनाए रखता है। इस तरह के क्रम को हमेशा उसी मॉड्यूल फ़ॉर्मेट में सत्यापित करें जिसे आप वास्तव में तैनात करते हैं।
इवेंट लूप एक ही थ्रेड पर चलता है: कोई भी लंबी सिंक्रोनस गणना - किसी बड़े पेलोड का JSON.parse, एक विनाशकारी रेगुलर एक्सप्रेशन, सिंक्रोनस एन्क्रिप्शन - चल रही सभी रिक्वेस्ट को जमा देती है। प्रोडक्शन में, monitorEventLoopDelay से लूप की लेटेंसी मापें: कुछ दसियों मिलीसेकंड से ऊपर का p99 किसी रुकावट का संकेत देता है। नीचे दिया गया कोड जानबूझकर एक रुकावट पैदा करता है और फिर उसे मापता है।
import { monitorEventLoopDelay }
from "node:perf_hooks";
const h = monitorEventLoopDelay({ resolution: 10 });
h.enable();
const end = Date.now() + 200;
while (Date.now() < end) {}
setTimeout(() => {
h.disable();
const ms = h.max / 1e6;
console.log(`max delay: ${ms.toFixed(1)} ms`);
}, 300);