ในโมดูลการเขียนโปรแกรมแบบดั้งเดิมการดำเนินการ I/O เป็นเหมือนการเรียกใช้ฟังก์ชั่นท้องถิ่นทั่วไป: โปรแกรมจะถูกบล็อกก่อนที่จะดำเนินการฟังก์ชั่นและไม่สามารถทำงานต่อไปได้ I/O ที่ถูกบล็อกมาจากโมเดล Slice Time ก่อนหน้านี้ซึ่งแต่ละกระบวนการเป็นเหมือนบุคคลอิสระโดยมีจุดประสงค์ในการแยกแยะทุกคนและทุกคนมักจะทำสิ่งเดียวในเวลาเดียวกันและต้องรอสิ่งที่ต้องทำก่อนหน้านี้ก่อนตัดสินใจว่าจะทำอะไรต่อไป อย่างไรก็ตามรุ่นนี้ของ "ผู้ใช้หนึ่งกระบวนการหนึ่งกระบวนการ" ที่ใช้กันอย่างแพร่หลายในเครือข่ายคอมพิวเตอร์และอินเทอร์เน็ตสามารถปรับขนาดได้มาก เมื่อจัดการหลายกระบวนการมันใช้หน่วยความจำและการสลับบริบทจำนวนมากจะใช้ทรัพยากรจำนวนมาก สิ่งเหล่านี้เป็นภาระอย่างมากในระบบปฏิบัติการและเมื่อจำนวนกระบวนการเพิ่มขึ้นประสิทธิภาพของระบบจะสลายตัวอย่างรวดเร็ว
มัลติเธรดเป็นทางเลือก เธรดเป็นกระบวนการที่มีน้ำหนักเบาที่แชร์หน่วยความจำกับเธรดอื่น ๆ ในกระบวนการเดียวกัน มันเป็นเหมือนส่วนขยายของโมเดลดั้งเดิมซึ่งใช้ในการดำเนินการหลายเธรดพร้อมกัน เมื่อเธรดหนึ่งกำลังรอการดำเนินการ I/O เธรดอื่น ๆ สามารถเข้าครอบครอง CPU ได้ เมื่อการดำเนินการ I/O เสร็จสิ้นเธรดที่รออยู่ด้านหน้าจะถูกปลุก กล่าวคือเธรดที่ทำงานสามารถถูกขัดจังหวะแล้วกลับมาทำงานต่อในภายหลัง นอกจากนี้เธรดสามารถทำงานแบบขนานภายใต้คอร์ที่แตกต่างกันของซีพียูหลายคอร์ภายใต้ระบบบางระบบ
โปรแกรมเมอร์ไม่ทราบว่าเธรดจะทำงานกี่โมง พวกเขาจะต้องระมัดระวังในการจัดการการเข้าถึงหน่วยความจำที่ใช้ร่วมกันพร้อมกันดังนั้นพวกเขาจะต้องใช้การซิงโครไนซ์ดั้งเดิมเพื่อซิงโครไนซ์การเข้าถึงโครงสร้างข้อมูลบางอย่างเช่นการใช้ล็อคหรือสัญญาณเพื่อบังคับให้เธรดดำเนินการในพฤติกรรมและแผนเฉพาะ แอปพลิเคชันที่พึ่งพาสถานะที่ใช้ร่วมกันระหว่างเธรดสามารถมีปัญหาแปลก ๆ ได้อย่างง่ายดายด้วยการสุ่มที่แข็งแกร่งและความยากลำบากในการค้นหา
อีกวิธีหนึ่งคือการใช้การทำงานร่วมกันแบบมัลติเธรดซึ่งคุณต้องรับผิดชอบในการปล่อยซีพียูอย่างชัดเจนและส่งเวลา CPU ให้กับเธรดอื่น ๆ อย่างชัดเจน เนื่องจากคุณควบคุมแผนการดำเนินการของเธรดเป็นการส่วนตัวความจำเป็นในการซิงโครไนซ์จะลดลง แต่มันก็เพิ่มความซับซ้อนของโปรแกรมและโอกาสของข้อผิดพลาดและไม่หลีกเลี่ยงปัญหาของมัลติเธรด
การเขียนโปรแกรมที่ขับเคลื่อนด้วยเหตุการณ์คืออะไร
การเขียนโปรแกรมที่ขับเคลื่อนด้วยเหตุการณ์เป็นรูปแบบการเขียนโปรแกรมที่เหตุการณ์กำหนดกระบวนการดำเนินการของโปรแกรม กิจกรรมได้รับการจัดการโดยตัวจัดการเหตุการณ์หรือการโทรกลับเหตุการณ์ การโทรกลับของเหตุการณ์เป็นฟังก์ชั่นที่เรียกว่าเมื่อเหตุการณ์เฉพาะเกิดขึ้นเช่นฐานข้อมูลส่งคืนผลลัพธ์การสืบค้นหรือผู้ใช้คลิกปุ่ม
โปรดจำไว้ว่าในโหมดการเขียนโปรแกรม I/O แบบดั้งเดิมที่ถูกบล็อกแบบสอบถามฐานข้อมูลอาจมีลักษณะเช่นนี้:
การคัดลอกรหัสมีดังนี้:
result = query ('เลือก * จากโพสต์โดยที่ id = 1');
do_something_with (ผลลัพธ์);
ฟังก์ชั่นการสืบค้นด้านบนจะทำให้เธรดหรือกระบวนการปัจจุบันอยู่ในสถานะรอจนกว่าฐานข้อมูลพื้นฐานจะเสร็จสิ้นการดำเนินการแบบสอบถามและส่งคืน
ในรูปแบบที่ขับเคลื่อนด้วยเหตุการณ์แบบสอบถามนี้จะกลายเป็นเช่นนี้:
การคัดลอกรหัสมีดังนี้:
query_finished = function (ผลลัพธ์) {
do_something_with (ผลลัพธ์);
-
แบบสอบถาม ('เลือก * จากโพสต์ที่ id = 1', query_finished);
ก่อนอื่นคุณกำหนดฟังก์ชั่นที่เรียกว่า query_finished ซึ่งมีสิ่งที่ต้องทำหลังจากการสืบค้นเสร็จสิ้น จากนั้นส่งฟังก์ชั่นนี้เป็นพารามิเตอร์ไปยังฟังก์ชั่นการสืบค้น query_finished จะถูกเรียกหลังจากการดำเนินการแบบสอบถามแทนที่จะส่งกลับผลลัพธ์การสืบค้น
เมื่อเหตุการณ์ที่คุณสนใจเกิดขึ้นฟังก์ชั่นที่คุณกำหนดจะถูกเรียกแทนที่จะส่งคืนค่าผลลัพธ์ รูปแบบการเขียนโปรแกรมนี้เรียกว่าการเขียนโปรแกรมที่ขับเคลื่อนด้วยเหตุการณ์หรือการเขียนโปรแกรมแบบอะซิงโครนัส นี่เป็นหนึ่งในคุณสมบัติที่ชัดเจนที่สุดของโหนด รูปแบบการเขียนโปรแกรมนี้หมายความว่ากระบวนการปัจจุบันจะไม่ถูกบล็อกเมื่อดำเนินการ I/O การดำเนินการ ดังนั้นการดำเนินการ I/O หลายรายการสามารถดำเนินการแบบขนานและฟังก์ชันการโทรกลับที่สอดคล้องกันจะถูกเรียกหลังจากการดำเนินการเสร็จสมบูรณ์
ชั้นพื้นฐานของการเขียนโปรแกรมที่ขับเคลื่อนด้วยเหตุการณ์ขึ้นอยู่กับลูปเหตุการณ์ ลูปเหตุการณ์นั้นเป็นโครงสร้างที่การตรวจจับเหตุการณ์และโปรเซสเซอร์เหตุการณ์กระตุ้นการโทรลูปอย่างต่อเนื่องของฟังก์ชั่นทั้งสองนี้ ในแต่ละลูปกลไกการวนรอบเหตุการณ์จำเป็นต้องตรวจสอบเหตุการณ์ที่เกิดขึ้น เมื่อเหตุการณ์เกิดขึ้นจะพบฟังก์ชันการโทรกลับที่สอดคล้องกันและเรียกมัน
Event Loop เป็นเพียงเธรดที่ทำงานอยู่ในกระบวนการ เมื่อมีเหตุการณ์เกิดขึ้นโปรเซสเซอร์เหตุการณ์สามารถทำงานคนเดียวและจะไม่ถูกขัดจังหวะนั่นคือ:
1. ฟังก์ชั่นการโทรกลับเหตุการณ์ส่วนใหญ่กำลังทำงานในช่วงเวลาที่กำหนด
2. ไม่มีโปรเซสเซอร์เหตุการณ์ถูกขัดจังหวะเมื่อทำงาน
ด้วยวิธีนี้นักพัฒนาไม่สามารถปวดหัวได้อีกต่อไปเกี่ยวกับการซิงโครไนซ์เธรดและการปรับเปลี่ยนพร้อมกันของหน่วยความจำที่ใช้ร่วมกัน
ความลับที่รู้จักกันดี:
นานมาแล้วผู้คนในชุมชนการเขียนโปรแกรมระบบรู้ว่าการเขียนโปรแกรมที่ขับเคลื่อนด้วยเหตุการณ์เป็นวิธีที่ดีที่สุดในการสร้างบริการที่เกิดขึ้นพร้อมกันสูงเพราะไม่จำเป็นต้องบันทึกบริบทจำนวนมากดังนั้นจึงบันทึกหน่วยความจำจำนวนมาก
ช้าแนวคิดนี้แทรกซึมเข้าไปในแพลตฟอร์มและชุมชนอื่น ๆ นอกจากนี้ยังมีการใช้งานและภาษาอื่น ๆ อีกมากมาย
ในการพัฒนาเฟรมเวิร์กเหล่านี้คุณต้องเรียนรู้ความรู้เฉพาะที่เกี่ยวข้องกับเฟรมเวิร์กและเฟรมเวิร์กเฉพาะห้องสมุด ตัวอย่างเช่นเมื่อใช้ Event Machine เพื่อรับประโยชน์จากการไม่ปิดกั้นคุณต้องหลีกเลี่ยงการใช้ไลบรารีคลาสแบบซิงโครนัสและสามารถใช้ไลบรารีคลาสแบบอะซิงโครนัสของ Event Machine เท่านั้น หากคุณใช้ไลบรารีการปิดกั้นใด ๆ (เช่นไลบรารีมาตรฐานของทับทิมส่วนใหญ่) เซิร์ฟเวอร์ของคุณจะสูญเสียความสามารถในการปรับขนาดที่ดีที่สุดเนื่องจากลูปเหตุการณ์จะยังคงถูกบล็อกอย่างต่อเนื่องบล็อกการประมวลผลของเหตุการณ์ I/O เป็นครั้งคราว
Node ได้รับการออกแบบมาเป็นแพลตฟอร์มเซิร์ฟเวอร์ I/O ที่ไม่ปิดกั้นดังนั้นโดยทั่วไปคุณควรคาดหวังว่ารหัสทั้งหมดที่ทำงานอยู่นั้นไม่ปิดกั้น เนื่องจากจาวาสคริปต์มีขนาดเล็กมากและไม่บังคับให้รุ่น I/O ใด ๆ (เนื่องจากไม่มีไลบรารีคลาส I/O มาตรฐาน) โหนดจึงถูกสร้างขึ้นในสภาพแวดล้อมที่บริสุทธิ์มากและจะไม่มีปัญหามรดก
วิธีที่โหนดและจาวาสคริปต์ทำให้แอปพลิเคชันแบบอะซิงโครนัสง่ายขึ้น
ผู้เขียนของ Node Ryan Dahl ใช้ C ในขั้นต้นเพื่อพัฒนาโครงการนี้ แต่พบว่าบริบทของการบำรุงรักษาการเรียกใช้ฟังก์ชันนั้นซับซ้อนเกินไปส่งผลให้มีความซับซ้อนของรหัสสูง จากนั้นเขาก็เปลี่ยนมาใช้ Lua แต่ Lua มีห้องสมุด I/O บล็อกอยู่แล้ว การผสมผสานระหว่างการปิดกั้นและการไม่ปิดกั้นอาจทำให้นักพัฒนาสับสนและทำให้ผู้คนจำนวนมากไม่สามารถสร้างแอพพลิเคชั่นที่ปรับขนาดได้ ดังนั้น Lua จึงถูกทอดทิ้งโดย Dahl ในที่สุดเขาก็หันไปหา JavaScript การปิดใน JavaScript และฟังก์ชั่นของวัตถุระดับแรกซึ่งทำให้ JavaScript เหมาะอย่างยิ่งสำหรับการเขียนโปรแกรมที่ขับเคลื่อนด้วยเหตุการณ์ ความมหัศจรรย์ของ JavaScript เป็นหนึ่งในเหตุผลหลักว่าทำไมโหนดจึงเป็นที่นิยม
การปิดคืออะไร
การปิดสามารถเข้าใจได้ว่าเป็นฟังก์ชั่นพิเศษ แต่สามารถสืบทอดและเข้าถึงตัวแปรในขอบเขตที่กำหนดไว้ เมื่อคุณผ่านฟังก์ชั่นการเรียกกลับเป็นพารามิเตอร์ไปยังฟังก์ชั่นอื่นมันจะถูกเรียกในภายหลัง เวทมนตร์คือเมื่อฟังก์ชั่นการโทรกลับนี้เรียกในภายหลังมันจะจดจำบริบทที่กำหนดตัวเองและตัวแปรในบริบทของผู้ปกครองและยังสามารถเข้าถึงได้ตามปกติ คุณสมบัติที่ทรงพลังนี้เป็นแกนหลักของความสำเร็จของโหนด
ตัวอย่างต่อไปนี้จะแสดงให้เห็นว่าการปิด JavaScript ทำงานอย่างไรในเว็บเบราว์เซอร์ หากคุณต้องการฟังเหตุการณ์แบบสแตนด์อโลนบนปุ่มคุณสามารถทำได้:
การคัดลอกรหัสมีดังนี้:
var clickCount = 0;
document.getElementById ('myButton'). onClick = function () {
ClickCount += 1;
การแจ้งเตือน ("คลิก" + ClickCount + "Times.");
-
นี่คือวิธีที่ใช้ jQuery:
การคัดลอกรหัสมีดังนี้:
var clickCount = 0;
$ ('ปุ่ม#myButton') คลิก (ฟังก์ชัน () {
ClickedCount ++;
การแจ้งเตือน ('คลิก' + ClickCount + 'Times.');
-
ใน JavaScript ฟังก์ชั่นเป็นวัตถุประเภทแรกซึ่งหมายความว่าคุณสามารถส่งฟังก์ชั่นเป็นพารามิเตอร์ไปยังฟังก์ชั่นอื่น ๆ ในสองตัวอย่างข้างต้นอดีตกำหนดฟังก์ชั่นให้กับฟังก์ชั่นอื่นและหลังผ่านฟังก์ชันเป็นพารามิเตอร์ไปยังฟังก์ชันอื่น ฟังก์ชั่นการประมวลผลเหตุการณ์คลิก (ฟังก์ชั่นการโทรกลับ) สามารถเข้าถึงแต่ละตัวแปรภายใต้บล็อกรหัสที่ฟังก์ชั่นกำหนด ในตัวอย่างนี้สามารถเข้าถึงตัวแปร clickcount ที่กำหนดไว้ในการปิดพาเรนต์
ตัวแปร ClickCount อยู่ในขอบเขตทั่วโลก (ขอบเขตนอกสุดใน JavaScript) ซึ่งจะช่วยประหยัดจำนวนครั้งที่ผู้ใช้คลิกปุ่ม โดยปกติแล้วจะเป็นนิสัยที่ไม่ดีในการจัดเก็บตัวแปรภายใต้ขอบเขตทั่วโลกเพราะมันง่ายที่จะขัดแย้งกับรหัสอื่น ๆ และคุณควรใส่ตัวแปรในขอบเขตท้องถิ่นที่คุณใช้ เวลาส่วนใหญ่เพียงแค่ห่อรหัสด้วยฟังก์ชั่นเดียวเทียบเท่ากับการสร้างการปิดอีกครั้งซึ่งสามารถหลีกเลี่ยงการก่อมลพิษต่อสภาพแวดล้อมทั่วโลกเช่นนี้:
การคัดลอกรหัสมีดังนี้:
(การทำงาน() {
var clickCount = 0;
$ ('ปุ่ม#myButton') คลิก (ฟังก์ชัน () {
ClickCount ++;
การแจ้งเตือน ('คลิก' + ClickCount + 'Times.');
-
-
หมายเหตุ: บรรทัดที่เจ็ดของรหัสด้านบนกำหนดฟังก์ชั่นและเรียกใช้ทันที นี่เป็นรูปแบบการออกแบบทั่วไปใน JavaScript: สร้างขอบเขตใหม่โดยการสร้างฟังก์ชั่น
การปิดการเขียนโปรแกรมแบบอะซิงโครนัสช่วยได้อย่างไร
ในรูปแบบการเขียนโปรแกรมที่ขับเคลื่อนด้วยเหตุการณ์ก่อนอื่นเขียนรหัสเพื่อเรียกใช้หลังจากเหตุการณ์เกิดขึ้นจากนั้นใส่รหัสลงในฟังก์ชั่นและในที่สุดก็ส่งฟังก์ชั่นเป็นพารามิเตอร์ไปยังผู้โทรจากนั้นเรียกมันโดยฟังก์ชันผู้โทรในภายหลัง
ใน JavaScript ฟังก์ชั่นไม่ใช่คำจำกัดความที่แยกได้ นอกจากนี้ยังจำบริบทของขอบเขตที่ประกาศไว้ กลไกนี้ช่วยให้ฟังก์ชั่น JavaScript สามารถเข้าถึงบริบทที่นิยามฟังก์ชั่นอยู่และตัวแปรทั้งหมดในบริบทหลัก
เมื่อคุณผ่านฟังก์ชั่นการเรียกกลับเป็นพารามิเตอร์ไปยังผู้โทรฟังก์ชั่นจะถูกเรียกในบางจุดในภายหลัง แม้ว่าขอบเขตที่กำหนดฟังก์ชั่นการโทรกลับสิ้นสุดลงเมื่อเรียกใช้ฟังก์ชันการโทรกลับมันก็ยังสามารถเข้าถึงตัวแปรทั้งหมดในขอบเขตสิ้นสุดและขอบเขตหลัก เช่นตัวอย่างสุดท้ายฟังก์ชั่นการโทรกลับถูกเรียกภายในคลิก () ของ jQuery แต่ยังสามารถเข้าถึงตัวแปร ClickCount ได้
ความมหัศจรรย์ของการปิดแสดงไว้ก่อนหน้านี้ การผ่านตัวแปรสถานะไปยังฟังก์ชั่นช่วยให้คุณสามารถทำการเขียนโปรแกรมที่ขับเคลื่อนด้วยเหตุการณ์โดยไม่ต้องรักษาสถานะ กลไกการปิดของ JavaScript จะช่วยให้คุณดูแลรักษาได้
สรุป
การเขียนโปรแกรมที่ขับเคลื่อนด้วยเหตุการณ์เป็นรูปแบบการเขียนโปรแกรมที่กำหนดกระบวนการดำเนินการโปรแกรมผ่านการเรียกเหตุการณ์ โปรแกรมเมอร์ลงทะเบียนฟังก์ชั่นการโทรกลับสำหรับเหตุการณ์ที่พวกเขาสนใจ (โดยปกติเรียกว่าตัวจัดการเหตุการณ์) และจากนั้นระบบจะเรียกตัวจัดการเหตุการณ์ที่ลงทะเบียนเมื่อเหตุการณ์เกิดขึ้น รูปแบบการเขียนโปรแกรมนี้มีข้อได้เปรียบมากมายที่โมเดลการบล็อกการบล็อกแบบดั้งเดิมไม่มี ในอดีตเพื่อนำไปใช้คุณสมบัติที่คล้ายกันต้องใช้หลายกระบวนการ/มัลติเธรด
JavaScript เป็นภาษาที่ทรงพลังเนื่องจากฟังก์ชั่นและคุณสมบัติการปิดของวัตถุประเภทแรกทำให้เหมาะมากสำหรับการเขียนโปรแกรมที่ขับเคลื่อนด้วยเหตุการณ์