ดำดิ่งสู่ Javascript Internals
การทำงานภายในของเอ็นจิ้น Javascript, Event loop, Web APIs, คิวเหตุการณ์ & การเรียกกลับ
การแนะนำ
คุณเคยคิดไหมว่าจะเกิดอะไรขึ้นถ้าไม่มี Javascript ? หากไม่มี Javascript คุณจะไม่สามารถสตรีมภาพยนตร์บน Netflix ซื้อของใน Amazon หรือดูวิดีโอบน Youtube ได้ เกือบทุกเว็บไซต์อื่น ๆ ใช้ Javascript และทำให้หน้าเว็บเป็นแบบโต้ตอบ
ก่อนการคิดค้น Javascript อินเทอร์เน็ตประกอบด้วยหน้าHTML ธรรมดา HTML & CSSให้โครงสร้างและสไตล์แก่เว็บเพจ แต่ Javascript ทำให้หน้าเว็บเป็นไดนามิก คุณสามารถป้อนข้อมูลรับรองของคุณและเข้าสู่ระบบเว็บไซต์ ทำธุรกรรมออนไลน์ หรือเล่นเกม Javascriptทำให้ทุกอย่างเกิดขึ้น
หนึ่งในการสำรวจที่จัดทำโดย Stack-overflow แสดงให้เห็นว่า Javascript มีจำนวน repos สูงสุดบน GitHub ทุกๆ ปี เฟรมเวิร์กและไลบรารีใหม่ๆ ได้รับการพัฒนาใน Javascript ภาษาได้เปลี่ยนแปลงอินเทอร์เน็ตและทำให้ชีวิตของผู้คนง่ายขึ้นอย่างแท้จริง
พื้นหลัง
เมื่อ 2 ปีที่แล้ว ผมกำลังทำไซด์โปรเจกต์และใช้ Javascript เป็นครั้งแรก การเป็นโปรแกรมเมอร์ที่ช่ำชองตั้งแต่ทศวรรษที่ผ่านมา ฉันเริ่มคุ้นเคยกับไวยากรณ์และพัฒนาคำสั่งที่ดีภายในสามเดือน
เวลาผ่านไปหลายเดือน หน้าเว็บที่ฉันพัฒนาทำงานได้ไม่ดีนัก ใช้เวลานานในการโหลดและเริ่มส่งผลกระทบต่อลูกค้า ดังนั้นฉันจึงได้ดำน้ำลึกเพื่อระบุสาเหตุที่แท้จริง
หลังจากอดหลับอดนอนมาทั้งคืน ในที่สุดฉันก็หมดปัญญาและกำลังจะยอมแพ้ อย่างไรก็ตาม ฉันตัดสินใจที่จะลองครั้งสุดท้าย ในที่สุด ฉันคิดว่าการทำความเข้าใจอย่างถ่องแท้ว่า Javascript ทำงานภายในองค์กรอย่างไร ฉันอ้างอิงแหล่งข้อมูลจำนวนมากและในที่สุดก็สามารถแก้ไขข้อบกพร่องในโค้ดของฉันได้ภายในสองวัน
วิศวกรมักจะมุ่งเน้นไปที่การสร้างสิ่งใหม่ๆ เครื่องมือและเฟรมเวิร์กที่เราใช้ซ่อนความซับซ้อนส่วนใหญ่จากเรา ในการเดินทางของเรา เรามักจะลืมว่าเบื้องหลังการทำงานเป็นอย่างไร จำเป็นต้องรู้การทำงานภายในของเครื่องมือที่เราใช้ การมีความเข้าใจที่ดีเกี่ยวกับปัจจัยภายในช่วยให้เราตีกรอบปัญหาให้แคบลงและหาแนวทางแก้ไขที่นำไปใช้ได้จริง
ในบทความนี้ เราจะดูว่าเกิดอะไรขึ้นเบื้องหลังเมื่อ Javascript ถูกเรียกใช้งานโดยเบราว์เซอร์ เราจะดูที่เครื่องมือ Javascript และทำความเข้าใจว่ามันใช้งานได้ เราจะเห็นองค์ประกอบต่างๆ ที่เกี่ยวข้องในรันไทม์ Javascript และทำความเข้าใจเกี่ยวกับ Event Loop และคิวเหตุการณ์ แม้ว่าคำศัพท์เหล่านี้อาจใหม่สำหรับคุณ แต่บทความจะพยายามทำให้แต่ละคำง่ายขึ้น เรามาเริ่มกันที่ภารกิจของเราเพื่อฝึกฝน Javascript ภายในให้เชี่ยวชาญ
จาวาสคริปต์
Javascript เป็นภาษาตีความระดับสูง ไม่เหมือนกับ C หรือ C++ เราไม่ต้องคอมไพล์โค้ด Javascript แล้วเรียกใช้ โค้ด Javascript ถูกคอมไพล์เป็น bytecode ก่อนแล้วจึงแปลเป็นโค้ดระดับเครื่อง
ภาษาระดับสูง เช่น Java, C, C++, C# รองรับการทำงานแบบมัลติเธรด ภาษาเหล่านี้อนุญาตให้โปรแกรมเมอร์สร้างหลายเธรดและเรียกใช้งานในเธรดต่างๆ ดังนั้นเราจึงสามารถวางงานบน CPU หลายตัวและใช้ทรัพยากรคอมพิวเตอร์ได้อย่างมีประสิทธิภาพ
ในทางตรงกันข้าม Javascript เป็นเธรดเดี่ยว ซึ่งหมายความว่ามีการดำเนินการเพียงเธรดเดียวในช่วงเวลาใดก็ตาม ในกรณีที่คุณเรียกใช้ฟังก์ชันจาวาสคริปต์ เว็บเพจจะค้าง คุณอาจเห็นว่าเว็บไซต์ไม่ตอบสนองผิดพลาดหลายครั้งขณะเรียกดู
มาทำการสาธิตอย่างรวดเร็วและดูการทำงานจริง ฉันเปิด Twitter และฉันสามารถคลิกที่แท็บแต่ละแท็บ เช่นTrending, News, Sportsเป็นต้น
ตอนนี้ ในdeveloperคอนโซลของฉัน ฉันกำลังเขียนฟังก์ชันที่มีการวนซ้ำไม่สิ้นสุด ด้านล่างนี้คือคำจำกัดความของฟังก์ชัน
function infiniteLoop() {
for(i=0; i < 100000; ) {
}
}
infiniteLoop()
ด้านล่าง GIF แสดง ว่าฉันไม่สามารถเปลี่ยนแท็บเป็นSportsหรือEntertaintment
เนื่องจากฟังก์ชันมีการวนซ้ำไม่สิ้นสุด มันจะทำงานต่อไปอย่างไม่มีที่สิ้นสุด เมื่อฉันเลือกแท็บต่างๆ เบราว์เซอร์ควรจัดการเหตุการณ์clickและแสดงเนื้อหาของแท็บที่คลิกตามลำดับ ฉันไม่สามารถเลือกแท็บอื่นได้เนื่องจาก Javascript กำลังเรียกใช้ฟังก์ชันนี้อยู่infiniteLoopแล้ว
ดังที่เห็นในตัวอย่างข้างต้น เราสามารถเรียกใช้ฟังก์ชันใน Javascript ได้ครั้งละหนึ่งฟังก์ชันเท่านั้น ตอนนี้คุณอาจสงสัยว่าเบราว์เซอร์สามารถโหลดเว็บไซต์ที่ซับซ้อนได้อย่างไรในเสี้ยววินาที
เราจะเข้าใจในส่วนต่อๆ ไปว่า Javascript อยู่เบื้องหลังเวทมนตร์นี้อย่างไร ตอนนี้เรามาทำความเข้าใจถึงแก่นของ Javascript นั่นคือ Javascript Engine
โปรแกรม Javascript
ทุกเบราว์เซอร์มี Javascript Engine ของตัวเอง ตัวอย่างเช่น: - Chrome ใช้เครื่องมือ V8 ในขณะที่ Firefox ใช้ SpiderMonkey Engine Engine มีหน้าที่ดำเนินการโค้ด Javascript ทีละบรรทัด ต่อไปนี้เป็นรายการเบราว์เซอร์ยอดนิยมและเครื่องมือ Javascript
เอ็นจิ้น Javascript ประกอบด้วยHeapและStack มาทำความเข้าใจการทำงานขององค์ประกอบทั้งสองนี้กันเถอะ
กอง
ฮีปมีหน้าที่จัดเก็บนิยามของฟังก์ชัน, ตัวแปรอ็อบเจกต์ ประกอบด้วยวัตถุทั้งหมดที่ถูกอ้างอิงโดยรหัส
วัตถุถูกสร้างขึ้นแบบไดนามิกในฮีป การทำงานคล้ายกับจาวา เมื่อใดก็ตามที่วัตถุถูกอ้างอิงโดยฟังก์ชัน วัตถุนั้นจะถูกดึงมาจากฮีป
function bar() {
var player = { "firstName": "Shubhman",
"lastName": "Gill"
}
var teams = ["India", "NewZealand"]
i =0
a = 10
console.log("In the function bar")
}
bar()
Objects on Heap
ซ้อนกัน
Stack เป็นโครงสร้างข้อมูลแบบ Last In First Out (LIFO ) ทุกครั้งที่เรียกใช้ฟังก์ชัน ตัวแปรภายในของฟังก์ชันจะถูกพุชไปยังสแต็ก เมื่อฟังก์ชันดำเนินการเสร็จสิ้น ฟังก์ชันนั้นจะถูกลบออกจากสแต็ก หากฟังก์ชันเรียกใช้ฟังก์ชันใหม่ระหว่างการดำเนินการ ฟังก์ชันใหม่จะถูกพุชไปยังสแต็กด้วย Engine เรียกใช้ฟังก์ชันซึ่งอยู่ด้านบนสุดของสแต็ก
ฟังก์ชันที่อยู่ด้านล่างสุดของสแต็กกลับมาดำเนินการต่อเมื่อฟังก์ชันทั้งหมดด้านบนกลับมาทำงาน ในกรณีที่ฟังก์ชันส่งข้อผิดพลาด การติดตามสแต็กจะถูกพิมพ์ลงบนไฟล์console. การติดตามสแต็กประกอบด้วยสายโซ่ของฟังก์ชันซึ่งถูกเรียกใช้เมื่อเกิดข้อยกเว้น ลองมาดูตัวอย่างกัน
ฉันจะเรียกใช้รหัสข้างต้นในเบราว์เซอร์ของฉัน cityและ เพิ่มจุดพักในฟังก์ชัน
cityดังที่เห็นข้างต้น ในการเรียกใช้ งานสคริปต์ สคริปต์ได้หยุดที่เบรกพอยต์ในฟังก์ชัน ทางด้านขวา ในCall Stackส่วน คุณจะเห็นcityฟังก์ชันที่ด้านบน
ประกอบด้วยCall Stackฟังก์ชันทั้งหมดที่เรียกใช้จนถึงตอนนี้ และฟังก์ชั่นล่าสุดจะอยู่ด้านบนสุด เมื่อฟังก์ชันดำเนินการเสร็จสิ้น ฟังก์ชันนั้นจะถูกลบออกจากสแต็ก
คุณสามารถดูได้จากภาพด้านล่าง ฟังก์ชันcityถูกลบออกและการดำเนินการต่อไปสำหรับฟังก์ชันstreetที่บรรทัดหมายเลข 9
ตามที่เห็นข้างต้น Javascript สามารถดำเนินการได้ครั้งละหนึ่งฟังก์ชันเท่านั้น และมีเพียงหนึ่งเธรดของการดำเนินการเท่านั้น คุณไม่สามารถเรียกใช้หลายฟังก์ชันพร้อมกันในการดำเนินการสแต็ก
สมมติว่าเรามีฟังก์ชันที่ทำการโทรHTTPและรับข้อมูลจากเว็บไซต์อื่น จะเกิดอะไรขึ้นหากเซิร์ฟเวอร์ทำงานช้าและฟังก์ชันใช้เวลานานในการดำเนินการ เราจะไม่สามารถเรียกใช้ฟังก์ชันอื่นใดได้ และประสบการณ์ของผู้ใช้จะแย่มาก
สิ่งนี้เกิดขึ้นในโลกแห่งความเป็นจริงหรือไม่? ไม่ เว็บไซต์ที่ซับซ้อน เช่นFacebook.com , Twitter.com , Amazon.comและอื่น ๆ รันโค้ด Javascript จำนวนมากโดยไม่ส่งผลกระทบต่อประสบการณ์ของผู้ใช้ เพื่อให้เข้าใจถึงวิธีการ เราจะเจาะลึกลงไปในรันไทม์ของ Javascript
รันไทม์ Javascript
Javascript ให้ API ต่างๆ แก่เราเพื่อดึงข้อมูลจากเซิร์ฟเวอร์ จัดการ DOM หรือเรียกใช้ฟังก์ชันโดยใช้ตัวจับเวลา เนื่องจากลักษณะเธรดเดียวของ Javascript จึงสามารถหยุดการแสดงผลของเว็บไซต์ได้ ซึ่งส่งผลให้ประสิทธิภาพต่ำ อย่างไรก็ตาม รันไทม์ของ Javascript ช่วยให้มั่นใจได้ว่าสิ่งนี้จะไม่เกิดขึ้น ลองทำความเข้าใจกับตัวอย่างนี้
คุณสามารถเดาได้ว่าผลลัพธ์ของโค้ดด้านล่างจะเป็นอย่างไร:-
จะgetSubTitlesพิมพ์หลังจากพิมพ์getActors? แล้วโทรแล้วsetTimeoutรหัสจะหยุดไหม ?
ต่อไปนี้เป็นผลลัพธ์ที่เกิดจากการรันโค้ดด้านบน:-
ดังที่เห็นด้านบนgetSubTitlesพิมพ์ก่อนและ 4 วินาทีต่อมาgetActorsพิมพ์ แม้ว่าฟังก์ชันsetTimeoutจะรอ 4 วินาที แต่ก็ยังเรียกใช้งานฟังก์ชันที่ตามมาgetSubTitlesก่อนหน้าและโค้ดไม่รอ ตอนนี้เรามาทำความเข้าใจว่าเกิดอะไรขึ้นเบื้องหลัง
API ของเว็บ
คุณจะไม่พบการใช้งานฟังก์ชันต่างๆ เช่นsetTimeoutหรือfetchที่ใดก็ตามในซอร์สโค้ด Javascript ฟังก์ชันเหล่านี้มีให้โดยเบราว์เซอร์ ไม่ใช่ส่วนหนึ่งของ Javascript การใช้งานจะแตกต่างกันไปในแต่ละเบราว์เซอร์
เบราว์เซอร์มีฟังก์ชันในการดำเนินการเรียกเครือข่าย จัดการ DOM ประมวลผลเสียง วิดีโอ ฯลฯ ฟังก์ชันเหล่านี้สร้าง Web API การใช้งานภายในมีความซับซ้อนและประกอบด้วยโค้ด C++ เป็นหลัก API ให้อินเทอร์เฟซแก่เราซึ่งสามารถเรียกใช้จากรหัส Javascript
เมื่อคุณเรียกใช้ฟังก์ชัน เช่นsetTimeoutJavascript จะเข้าใจว่านี่คือ Web API จากนั้นมอบหมายการดำเนินการของฟังก์ชันนี้ให้กับคอนเทนเนอร์ Web API ฟังก์ชันนี้setTimeoutดำเนินการในเธรดแยกต่างหาก ไม่ได้ดำเนินการในเธรดการดำเนินการของ Javascript นี่คือเหตุผลว่าทำไมโค้ดที่เขียนหลังจากsetTimeoutถูกดำเนินการก่อนที่ฟังก์ชันการโทรกลับจะส่งผ่านไปยังโค้ดนั้น
ในทำนองเดียวกัน เนื่องจากการโทรผ่านเครือข่ายต้องใช้เวลา ฟังก์ชันต่างๆ เช่นfetchจะดำเนินการในเธรดแยกต่างหาก Web API ของเบราว์เซอร์ซ่อนความซับซ้อนส่วนใหญ่ไว้ไม่ให้เรารู้ ตัวอย่างเช่น:- การใช้งานภายในของfetchจะมีการสร้างการเชื่อมต่อ TCP การส่งข้อมูลและการแยกวิเคราะห์ นอกจากนี้ยังจะใช้ทรัพยากรการประมวลผลอย่างมีประสิทธิภาพด้วยการจัดตารางเวลาการดำเนินการบนเธรดที่เหมาะสม
gif ด้านล่างแสดงสิ่งที่เกิดขึ้นเมื่อคุณเรียกใช้ฟังก์ชัน Web API setTimeoutเช่น
ตามที่เห็นด้านบน เมื่อsetTimeoutฟังก์ชันถูกเรียกใช้ ฟังก์ชันนั้นจะส่งฟังก์ชันการเรียกกลับgetActorsไปยัง Web API ในคอนเทนเนอร์ Web APIs ฟังก์ชันsetTimeoutจะรอสี่วินาที ในขณะเดียวกัน ฟังก์ชันที่ตามมาgetSubTitlesจะถูกเรียกใช้
ตอนนี้คุณอาจถามว่าฟังก์ชันการโทรกลับgetActorsทำงานอย่างไรหลังจากหมดเวลา เราจะเห็นในส่วนถัดไปว่าการเรียกกลับจะดำเนินการอย่างไรหลังจาก Web API กลับมา
คิวงาน
Javascript อาศัยฟังก์ชันการโทรกลับเป็นอย่างมาก ฟังก์ชันการโทรกลับจะดำเนินการเมื่อมีเหตุการณ์เกิดขึ้น เช่น:- หลังจากได้รับการตอบกลับจากเซิร์ฟเวอร์ เราจำเป็นต้องพิมพ์jsonตัวแทน หรือเรียกใช้ฟังก์ชันหลังจากหน่วงเวลา 4 วินาที
รันไทม์ของ Javascript ประกอบด้วยส่วนประกอบที่เรียกว่า Event Queue หรือ Callback Queue เป็นคิวของข้อความและใช้คำสั่ง First In First Out (FIFO) ข้อความไม่มีอะไรนอกจากฟังก์ชั่นการโทรกลับที่กำหนดให้ดำเนินการ
ลองมาดูตัวอย่างเพื่อทำความเข้าใจสิ่งนี้ ในโค้ดด้านล่าง เรากำลังลงทะเบียนฟังก์ชันการโทรกลับเมื่อใดก็ตามที่มีเหตุการณ์การคลิกบนปุ่ม เมื่อผู้ใช้คลิกปุ่ม ฟังก์ชันonClickจะถูกเรียกใช้ ฟังก์ชันนี้เรียกใช้ฟังก์ชันtimerหลังจากหน่วงเวลา 2 วินาที
แอนิเมชั่นด้านบนแสดงสิ่งที่เกิดขึ้นเบื้องหลัง เรามี Web API สองตัวที่นี่ — a) $.onและ b setTimeout) API แรกรอให้เหตุการณ์การคลิกเกิดขึ้น เมื่อได้รับเหตุการณ์คลิก ก็จะดำเนินการonClickฟังก์ชัน API ที่สองsetTimeOutถูกเรียกใช้ในภายหลังโดยonClickฟังก์ชัน
ทุกครั้งที่ผู้ใช้คลิกปุ่ม ฟังก์ชันonClickจะถูกพุชใน Event Queue ในทำนองเดียวกัน หลังจากหมดเวลา ฟังก์ชันtimerจะถูกพุชในคิว ดังนั้นคิวเหตุการณ์จึงประกอบด้วยฟังก์ชันการโทรกลับทั้งหมดที่ต้องดำเนินการ ในกรณีนี้onClickและtimerฟังก์ชัน
วนรอบเหตุการณ์
ฟังก์ชันการเรียกกลับEventQueueเข้าถึงสแต็กการดำเนินการของ Javascript ผ่านทาง Event Loop ลูปเหตุการณ์จะตรวจสอบอย่างต่อเนื่องว่า Javascript Stack ปัจจุบันว่างเปล่าหรือไม่ หากพบว่าสแต็กว่างเปล่า ระบบจะเลือกข้อความแรกจาก Event Queue และส่งต่อไปยังสแต็ก
จากตัวอย่างในส่วนที่แล้ว ทันทีที่ Event Loop พบว่าสแต็กว่างเปล่า มันจะรับข้อความถัดไปจากคิวการเรียกกลับและดำเนินการ
ด้านล่างนี้เป็นรหัสหลอกคร่าวๆสำหรับการทำงานของ event loop :-
คุณสามารถนึกถึง Event loop ว่าเป็นลูปไม่สิ้นสุดที่ตรวจสอบสแต็กการดำเนินการอย่างต่อเนื่อง ในกรณีที่สแต็กว่าง มันจะรับข้อความ (ฟังก์ชัน) จาก Event Queue และดำเนินการ
ไดอะแกรมด้านล่างแสดงการแสดงรันไทม์ Javascript ในระดับสูง
เราได้ดูส่วนประกอบต่างๆ ในรันไทม์ของ Javascript ตอนนี้คุณจะมีความคิดที่ดีขึ้นว่า Javascript ทำงานอย่างไรภายใต้ประทุน มาสรุปสิ่งที่เราได้เรียนรู้มา
สรุป
- Javascript เป็นเธรดเดี่ยวและทำงานทีละฟังก์ชัน
- มันใช้ประโยชน์จาก Web API เพื่อดำเนินงานที่ซับซ้อนในเบื้องหลัง
- เมื่อการดำเนินการ Web API เสร็จสิ้น จะส่งฟังก์ชันการเรียกกลับไปยังคิวเหตุการณ์
- ลูปเหตุการณ์จะตรวจสอบอย่างต่อเนื่องว่าสแต็ก Javascript ว่างเปล่าหรือไม่ ในกรณีที่สแต็กว่าง มันจะเลือกฟังก์ชันการโทรกลับจากคิวเหตุการณ์และเรียกใช้
- Javascript ภายในและรันไทม์
- Javascript ทำงานอย่างไรในเบราว์เซอร์และโหนด
- คำแนะนำแบบเคลื่อนไหวเพื่อวนรอบเหตุการณ์ Node.js
- ห่าห่าเหตุการณ์คืออะไรอยู่แล้ว?
- Javascript แบบอะซิงโครนัสและเหตุการณ์วนซ้ำตั้งแต่เริ่มต้น





































![รายการที่เชื่อมโยงคืออะไร? [ส่วนที่ 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)