1. การวิเคราะห์เปิด
ในบทก่อนหน้าเราได้เรียนรู้ความรู้เชิงทฤษฎีพื้นฐานของ nodejs การทำความเข้าใจความรู้เชิงทฤษฎีนี้เป็นสิ่งสำคัญ ในบทที่ตามมาเราจะค่อยๆเรียนรู้โมดูลต่าง ๆ ในเอกสารอย่างเป็นทางการ ถึงเวลาแล้วที่ตัวเอกของบทความนี้จะปรากฏบนเวที ทั่วโลก
มาดูคำจำกัดความอย่างเป็นทางการ:
วัตถุทั่วโลกวัตถุเหล่านี้มีอยู่ในโมดูลทั้งหมด วัตถุเหล่านี้บางส่วนไม่ได้อยู่ในขอบเขตทั่วโลก แต่อยู่ในขอบเขตโมดูล - จะถูกบันทึกไว้
วัตถุเหล่านี้มีอยู่ในโมดูลทั้งหมด ในความเป็นจริงวัตถุบางอย่างไม่ได้อยู่ในขอบเขตขอบเขตทั่วโลก แต่อยู่ในขอบเขตโมดูล - สิ่งเหล่านี้จะถูกระบุ
ในเบราว์เซอร์ขอบเขตระดับบนสุดคือขอบเขตระดับโลก นั่นหมายความว่าในเบราว์เซอร์หากคุณอยู่ในขอบเขตของ Global var something จะกำหนดตัวแปรทั่วโลก
ในโหนดนี้แตกต่างกัน ขอบเขตระดับบนสุดไม่ใช่ขอบเขตทั่วโลก var something ภายในโมดูลโหนดจะเป็นแบบท้องถิ่นกับโมดูลนั้น
ฉันคิดว่าทุกคนไม่ควรไม่คุ้นเคยกับแนวคิดของวัตถุระดับโลก ในเบราว์เซอร์ขอบเขตระดับสูงสุดคือขอบเขตทั่วโลกซึ่งหมายความว่าถ้าคุณใช้ "VAR" เพื่อกำหนดตัวแปรในขอบเขตทั่วโลกตัวแปรนี้จะถูกกำหนดเป็นขอบเขตทั่วโลก
แต่มันแตกต่างกันใน nodejs ระดับสูงสุดของขอบเขตไม่ใช่ขอบเขตทั่วโลก ในโมดูลตัวแปรถูกกำหนดโดยใช้ "var" และตัวแปรนี้อยู่ในขอบเขตของโมดูลนี้เท่านั้น
ใน nodejs ตัวแปรฟังก์ชั่นหรือวิธีการที่กำหนดไว้ในโมดูลมีเฉพาะในโมดูลนั้น แต่สามารถส่งผ่านไปนอกโมดูลผ่านการใช้วัตถุส่งออก
อย่างไรก็ตามใน node.js ยังมีขอบเขตทั่วโลกนั่นคือคุณสามารถกำหนดตัวแปรฟังก์ชั่นหรือคลาสที่ไม่ต้องการโหลดโมดูลใด ๆ
ในขณะเดียวกันวิธีการทั่วโลกและวัตถุระดับโลกระดับโลกได้รับการกำหนดล่วงหน้าล่วงหน้าซึ่งเป็นเนมสเปซทั่วโลกใน NodeJS ตัวแปรส่วนกลางฟังก์ชั่นหรือวัตถุใด ๆ เป็นค่าแอตทริบิวต์ของวัตถุ
ในสภาพแวดล้อมการทำงานแบบแทนที่คุณสามารถสังเกตรายละเอียดในวัตถุทั่วโลกผ่านคำสั่งต่อไปนี้ดูรูปด้านล่าง:
ฉันจะพูดคุยเกี่ยวกับวัตถุค่าแอตทริบิวต์ที่เกี่ยวข้องที่ติดตั้งบนวัตถุทั่วโลกทีละด้านล่าง
(1) กระบวนการ
กระบวนการ {Object} วัตถุกระบวนการดูส่วนวัตถุกระบวนการ
กระบวนการ {Object} นี่คือวัตถุกระบวนการ ฉันจะอธิบายรายละเอียดในบทที่ตามมา แต่ที่นี่ฉันจะนำ API ออกมาก่อนเพื่อพูดคุยเกี่ยวกับเรื่องนี้
process.nexttick (โทรกลับ)
ในลูปถัดไปรอบเหตุการณ์ลูปโทรกลับนี้ นี่ไม่ใช่นามแฝงง่าย ๆ ในการ settimeout (fn, 0) มันมีประสิทธิภาพมากขึ้น โดยทั่วไปแล้วมันจะทำงานก่อนที่เหตุการณ์ I/O อื่น ๆ จะเกิดไฟ แต่มีข้อยกเว้นบางอย่าง ดู process.maxtickDepth ด้านล่าง
ฟังก์ชั่นการโทรกลับในลูปถัดไปของลูปเหตุการณ์ นี่ไม่ใช่นามแฝงง่ายๆสำหรับฟังก์ชั่น Settimeout (FN, 0) เพราะมันมีประสิทธิภาพมากกว่า
ฟังก์ชั่นนี้สามารถเรียกใช้ฟังก์ชันการโทรกลับของเราก่อนที่จะมี I/O ใด ๆ หากคุณต้องการดำเนินการบางอย่างหลังจากการสร้างวัตถุและก่อนการดำเนินการ I/O เกิดขึ้นฟังก์ชั่นนี้สำคัญมากสำหรับคุณ
หลายคนไม่เข้าใจการใช้งานกระบวนการ NEXTTICK () ใน Node.JS. มาดูกันว่า process.nexttick () คืออะไรและจะใช้งานได้อย่างไร
node.js เป็นเธรดเดี่ยว ยกเว้น System IO จะมีการประมวลผลเหตุการณ์เพียงครั้งเดียวในเวลาเดียวกันในระหว่างกระบวนการสำรวจเหตุการณ์ คุณสามารถนึกถึงการสำรวจเหตุการณ์เป็นคิวขนาดใหญ่ ณ เวลาแต่ละจุดระบบจะประมวลผลเหตุการณ์เดียวเท่านั้น
แม้ว่าคอมพิวเตอร์ของคุณจะมีคอร์ CPU หลายตัว แต่คุณไม่สามารถจัดการกับหลาย ๆ เหตุการณ์ในคู่ขนานได้ในเวลาเดียวกัน แต่คุณสมบัตินี้ทำให้ Node.js เหมาะสำหรับการประมวลผลแอปพลิเคชัน I/O แต่ไม่ใช่สำหรับแอปพลิเคชันคอมพิวเตอร์ CPU
ในแต่ละแอปพลิเคชัน I/O คุณจะต้องกำหนดฟังก์ชั่นการโทรกลับสำหรับแต่ละอินพุตและเอาต์พุตและพวกเขาจะถูกเพิ่มเข้ากับคิวการประมวลผลการสำรวจเหตุการณ์โดยอัตโนมัติ
เมื่อการดำเนินการ I/O เสร็จสมบูรณ์ฟังก์ชั่นการโทรกลับนี้จะถูกเรียกใช้ ระบบจะดำเนินการดำเนินการตามคำขออื่น ๆ ต่อไป
ในโหมดการประมวลผลนี้ process.nexttick () หมายถึงการกำหนดการดำเนินการและปล่อยให้การดำเนินการนี้ดำเนินการ ณ เวลาที่เกิดขึ้นเมื่อเหตุการณ์ต่อไป ลองมาดูตัวอย่าง ในตัวอย่างมี foo () ที่คุณต้องการโทรในเวลาถัดไปคุณสามารถทำได้:
การคัดลอกรหัสมีดังนี้:
ฟังก์ชั่น foo () {
console.error ('foo');
-
process.nexttick (foo);
console.error ('bar');
เรียกใช้รหัสด้านบนและคุณจะเห็นว่าเอาต์พุตของ "บาร์" อยู่หน้า "foo" สิ่งนี้ตรวจสอบคำสั่งข้างต้นว่า foo () ทำงานในเวลาถัดไป
การคัดลอกรหัสมีดังนี้:
บาร์
ฟู
นอกจากนี้คุณยังสามารถใช้ฟังก์ชั่น settimeout () เพื่อให้ได้เอฟเฟกต์การดำเนินการเดียวกัน:
การคัดลอกรหัสมีดังนี้:
settimeout (foo, 0);
console.log ('bar');
อย่างไรก็ตามในแง่ของกลไกการประมวลผลภายใน process.nexttick () และ settimeout (fn, 0) แตกต่างกัน process.nexttick () ไม่ใช่ความล่าช้าอย่างง่าย แต่มีคุณสมบัติมากขึ้น
แม่นยำยิ่งขึ้นการโทรที่กำหนดโดย process.nexttick () สร้าง substack ใหม่ ในสแต็คปัจจุบันคุณสามารถดำเนินการได้มากเท่าที่คุณต้องการ แต่เมื่อเรียกว่า Netxtick ฟังก์ชั่นจะต้องถูกส่งกลับไปยังสแต็กหลัก จากนั้นกลไกการสำรวจเหตุการณ์จะรอเหตุการณ์ใหม่ที่จะดำเนินการอีกครั้ง หากพบ Nexttick จะมีการสร้างสแต็กใหม่
มาดูกันว่าสถานการณ์ใดที่จะใช้ process.nexttick ():
งาน CPU CPU ที่ใช้งานมากในหลาย ๆ เหตุการณ์:
ในตัวอย่างต่อไปนี้มีการคำนวณ () เราหวังว่าฟังก์ชั่นนี้จะถูกดำเนินการอย่างต่อเนื่องที่สุดเท่าที่จะเป็นไปได้
แต่ในขณะเดียวกันเราก็หวังว่าระบบจะไม่ถูกบล็อกโดยฟังก์ชั่นนี้และจะสามารถตอบสนองและจัดการกับเหตุการณ์อื่น ๆ ได้ รูปแบบแอปพลิเคชันนี้เป็นเหมือนเซิร์ฟเวอร์บริการเว็บเธรดเดียว ที่นี่เราสามารถใช้ process.nexttick () เพื่อ cross-execute compute () และการตอบสนองเหตุการณ์ปกติ
การคัดลอกรหัสมีดังนี้:
var http = ต้องการ ('http');
ฟังก์ชันคำนวณ () {
// ดำเนินการคำนวณที่ซับซ้อนอย่างต่อเนื่อง
-
process.nexttick (คำนวณ);
-
http.createserver (ฟังก์ชั่น (req, res) {
res.writehead (200, {'content-type': 'text/plain'});
Res.end ('Hello World');
}). ฟัง (5000, '127.0.0.1');
คำนวณ();
ในโหมดนี้เราไม่จำเป็นต้องเรียก compute () ซ้ำ เราจำเป็นต้องใช้ process.nexttick () เพื่อกำหนด compute () เพื่อดำเนินการ ณ จุดถัดไปในเวลาในลูปเหตุการณ์
ในระหว่างกระบวนการนี้หากมีการร้องขอ HTTP ใหม่กลไกการวนซ้ำเหตุการณ์จะดำเนินการตามคำขอใหม่ก่อนแล้วจึงเรียก compute ()
ในทางตรงกันข้ามถ้าคุณใส่ Compute () ในการโทรแบบเรียกซ้ำระบบจะถูกบล็อกในการคำนวณ () และไม่สามารถประมวลผลคำขอ HTTP ใหม่ได้ คุณสามารถลองด้วยตัวเอง
แน่นอนว่าเราไม่สามารถได้รับประโยชน์ที่แท้จริงของการดำเนินการแบบขนานภายใต้ซีพียูหลายตัวผ่าน process.nexttick () ซึ่งเป็นเพียงการจำลองแอปพลิเคชันเดียวกันที่ถูกดำเนินการในเซ็กเมนต์ใน CPU
(2) คอนโซล
คอนโซล {Object} ใช้ในการพิมพ์ไปยัง stdout และ stderr.see ส่วน stdio
คอนโซล {Object} ใช้เพื่อพิมพ์ไปยังเอาต์พุตมาตรฐานและเอาต์พุตข้อผิดพลาด ดูการทดสอบต่อไปนี้:
การคัดลอกรหัสมีดังนี้:
console.log ("สวัสดี Bigbear!");
สำหรับ (var i ในคอนโซล) {
console.log (i+""+คอนโซล [i]);
-
จะได้รับผลลัพธ์ต่อไปนี้:
การคัดลอกรหัสมีดังนี้:
var log = function () {
process.stdout.write (format.apply (นี้, อาร์กิวเมนต์) + '/n');
-
var info = function () {
process.stdout.write (format.apply (นี้, อาร์กิวเมนต์) + '/n');
-
var warn = function () {
writeerror (format.apply (นี้, อาร์กิวเมนต์) + '/n');
-
ข้อผิดพลาด var = function () {
writeerror (format.apply (นี้, อาร์กิวเมนต์) + '/n');
-
var dir = ฟังก์ชั่น (วัตถุ) {
var util = ต้องการ ('util');
process.stdout.write (util.inspect (วัตถุ) + '/n');
-
var time = function (label) {
ครั้ง [label] = date.now ();
-
var timeend = function (label) {
ระยะเวลา var = date.now () - ครั้ง [label];
exports.log ('undefined: nanms', ฉลาก, ระยะเวลา);
-
var trace = function (label) {
// todo อาจทำได้ดีกว่านี้ด้วยวัตถุดีบั๊กของ V8
// ถูกเปิดเผย.
var err = ข้อผิดพลาดใหม่;
err.name = 'trace';
err.message = label || -
Error.CaptuRestackTrace (err, arguments.callee);
console.error (err.stack);
-
var assert = function (นิพจน์) {
ถ้า (! นิพจน์) {
var arr = array.prototype.slice.call (อาร์กิวเมนต์, 1);
ต้องการ ('ยืนยัน'). ตกลง (false, format.apply (นี่, arr));
-
-
ผ่านฟังก์ชั่นเหล่านี้โดยทั่วไปเรารู้ว่า NodeJS ได้เพิ่มอะไรในขอบเขตทั่วโลก ในความเป็นจริง APIs ที่เกี่ยวข้องในวัตถุคอนโซลจะห่อหุ้ม "stdout.write" บนวัตถุกระบวนการเท่านั้นและแขวนไว้ในวัตถุทั่วโลก
(3), การส่งออกและโมดูลส่งออก
ใน nodejs มีสองขอบเขตแบ่งออกเป็นขอบเขตและขอบเขตโมดูลทั่วโลก
การคัดลอกรหัสมีดังนี้:
var name = 'var-name';
ชื่อ = 'ชื่อ';
global.name = 'global-name';
this.name = 'module-name';
console.log (global.name);
console.log (this.name);
console.log (ชื่อ);
เราเห็นชื่อ var = 'var-name'; name = 'name'; เป็นตัวแปรท้องถิ่นที่กำหนดไว้
global.name = 'global-name'; กำหนดแอตทริบิวต์ชื่อสำหรับวัตถุทั่วโลก
และ this.name = 'module-name'; กำหนดแอตทริบิวต์ชื่อสำหรับวัตถุโมดูล
ดังนั้นเรามาตรวจสอบให้บันทึกต่อไปนี้เป็น test2.js แล้วเรียกใช้มัน
การคัดลอกรหัสมีดังนี้:
var t1 = ต้องการ ('./ test1');
console.log (t1.name);
console.log (global.name);
ดังที่เห็นได้จากผลลัพธ์เราประสบความสำเร็จในการนำเข้าโมดูล test1 และเรียกใช้รหัส test1 เนื่องจาก global.name เป็นเอาต์พุตใน test2
T1.NAME ถูกกำหนดไว้ในโมดูล test1 ผ่านทางนี้ชื่อนี้แสดงให้เห็นว่าสิ่งนี้ชี้ไปที่วัตถุขอบเขตโมดูล
ความแตกต่างเล็กน้อยระหว่างการส่งออกและโมดูลส่งออก
Module.exports เป็นอินเทอร์เฟซจริงและการส่งออกเป็นเพียงเครื่องมือเสริมสำหรับมัน การกลับมาสู่การโทรครั้งสุดท้ายคือ Module.exports ส่งออกแทนที่จะส่งออก
คุณสมบัติและวิธีการทั้งหมดที่รวบรวมโดยการส่งออกจะถูกกำหนดให้กับ Module.exports ส่งออก แน่นอนว่ามีข้อกำหนดเบื้องต้นสำหรับสิ่งนี้นั่นคือ Module.exports本身不具备任何属性和方法。
如果, Module.exports已经具备一些属性和方法,那么exports收集来的信息将被忽略。
ใช้เกาลัด:
สร้างไฟล์ใหม่ bb.js
การคัดลอกรหัสมีดังนี้:
exports.name = function () {
console.log ('ฉันชื่อ Big Bear!');
-
สร้างไฟล์ทดสอบ test.js
การคัดลอกรหัสมีดังนี้:
var bb = ต้องการ ('./ bb.js');
bb.name (); // 'ฉันชื่อ Big Bear! -
แก้ไข bb.js ดังนี้:
การคัดลอกรหัสมีดังนี้:
module.exports = 'bigbear!' -
exports.name = function () {
console.log ('ฉันชื่อ Big Bear!');
-
อ้างถึงการดำเนินการ bb.js อีกครั้ง
การคัดลอกรหัสมีดังนี้:
var bb = ต้องการ ('./ bb.js');
bb.name (); // ไม่มีวิธี 'ชื่อ'
จากนี้เราจะเห็นได้ว่าโมดูลของคุณไม่จำเป็นต้องส่งคืน "วัตถุที่กำหนด" โมดูลของคุณอาจเป็นวัตถุ JavaScript ตามกฎหมาย -Boolean, หมายเลข, วันที่, JSON, สตริง, ฟังก์ชั่น, อาร์เรย์ ฯลฯ
(4), settimeout, setInterval, process.nexttick, setimmediate
ต่อไปนี้อยู่ในรูปของบทสรุป
NODEJS มีลักษณะเป็นตัวขับเคลื่อนเหตุการณ์ที่เกิดขึ้นพร้อมกันซึ่งเกิดจาก I/O แบบอะซิงโครนัส เครื่องยนต์ที่สร้างคุณสมบัตินี้เป็นลูปเหตุการณ์ เหตุการณ์ถูกแบ่งออกเป็นผู้สังเกตการณ์เหตุการณ์ที่สอดคล้องกันเช่นผู้สังเกตการณ์ที่ไม่ได้ใช้งานผู้สังเกตการณ์จับเวลาผู้สังเกตการณ์ I/O ฯลฯ แต่ละลูปของลูปเหตุการณ์เรียกว่าเห็บ แต่ละเห็บจะนำเหตุการณ์ออกจากผู้สังเกตการณ์เหตุการณ์ตามลำดับสำหรับการประมวลผล
ตัวจับเวลาที่สร้างขึ้นเมื่อเรียก settimeout () หรือ setInterval () จะถูกวางไว้ในต้นไม้สีแดงและสีดำภายในผู้สังเกตการณ์ตัวจับเวลา ทุกครั้งที่คุณติ๊กมันจะตรวจสอบว่าตัวจับเวลามีเวลาเกินเวลาจากต้นไม้สีแดงและสีดำหรือไม่ หากเกินกำหนดเวลาฟังก์ชันการโทรกลับที่สอดคล้องกันจะถูกดำเนินการทันที Settimeout () และ setInterval () ถูกใช้โดยตัวจับเวลา ความแตกต่างคือหลังถูกทริกเกอร์ซ้ำ ๆ และเนื่องจากการตั้งค่าเวลาสั้นเกินไปการประมวลผลหลังจากทริกเกอร์ก่อนหน้านี้จะถูกทริกเกอร์ทันทีหลังจากทริกเกอร์ก่อนหน้านี้เสร็จสิ้น
เนื่องจากตัวจับเวลาเป็นทริกเกอร์การหมดเวลาสิ่งนี้จะลดความแม่นยำของทริกเกอร์ ตัวอย่างเช่นเวลาหมดเวลาที่ตั้งค่าด้วย SettimeOut คือ 5 วินาที เมื่อลูปเหตุการณ์ทำงานผ่านงานในวินาทีที่ 4 และเวลาดำเนินการคือ 3 วินาทีฟังก์ชั่นการโทรกลับ settimeout จะหมดอายุเป็นเวลา 2 วินาทีซึ่งเป็นสาเหตุของการลดความแม่นยำ และเนื่องจากตัวจับเวลาและการตัดสินทริกเกอร์จะถูกบันทึกไว้โดยใช้ต้นไม้สีแดงและสีดำและวิธีการวนซ้ำจึงเป็นการเสียประสิทธิภาพ
ฟังก์ชั่นการโทรกลับทั้งหมดที่ตั้งค่าโดยใช้ process.nexttick () จะถูกวางไว้ในอาร์เรย์และทั้งหมดจะถูกดำเนินการทันทีในครั้งต่อไปที่คุณติ๊ก การดำเนินการนี้มีน้ำหนักเบาและมีความแม่นยำสูง
ฟังก์ชั่นการโทรกลับที่ตั้งค่าโดย setimmediate () ยังเรียกว่าเห็บถัดไป ความแตกต่างระหว่างมันและ process.nexttick () คือสองจุด:
1. ลำดับความสำคัญของการดำเนินการของผู้สังเกตการณ์ที่พวกเขาอยู่นั้นแตกต่างกัน process.nexttick () เป็นของผู้สังเกตการณ์ที่ไม่ได้ใช้งาน setimmediate () เป็นของผู้สังเกตการณ์ตรวจสอบและลำดับความสำคัญของการใช้งาน> ตรวจสอบ
2. ฟังก์ชั่นการโทรกลับที่ตั้งค่าโดย setimmediate () ถูกวางไว้ในรายการที่เชื่อมโยงและการโทรกลับหนึ่งรายการในรายการที่เชื่อมโยงจะถูกดำเนินการในแต่ละครั้ง นี่คือเพื่อให้แน่ใจว่าทุกเห็บสามารถดำเนินการได้อย่างรวดเร็ว
ประการที่สองมาสรุปกันเถอะ
1. เข้าใจความหมายของการมีอยู่ของวัตถุระดับโลก
2. ความแตกต่างเล็กน้อยระหว่างการส่งออกและโมดูลส่งออก
3. เลเยอร์พื้นฐานของคอนโซลที่สร้างขึ้นคืออะไร (การห่อหุ้มระดับสูงของวัตถุกระบวนการ)
4. ความแตกต่างระหว่าง settimeout, setInterval, process.nexttick, setimmediate
5. สองขอบเขตใน nodejs