วิธีเริ่มต้นใช้งาน VUE3.0 อย่างรวดเร็ว: เรียนรู้
เกี่ยวกับบริบทการดำเนินการ สแต็กการดำเนินการ และกลไกการดำเนินการ (งานแบบซิงโครนัส งานแบบอะซิงโครนัส งานไมโครทาสก์ มาโครทาสก์ และลูปเหตุการณ์) ใน js
ซึ่งเป็นจุดทดสอบที่พบบ่อยในการสัมภาษณ์ และ เพื่อนบางคนอาจรู้สึกสับสนเมื่อถามดังนั้นฉันจะสรุปให้วันนี้หวังว่าจะเป็นประโยชน์กับคุณต่อหน้าหน้าจอ
ก่อนที่จะพูดถึงบริบทการดำเนินการและกลไกการดำเนินการ js
ใน js
เรามาพูดถึงเธรดและกระบวนการกันดี
ในแง่อย่างเป็นทางการ线程
คือหน่วยที่เล็กที่สุดของการตั้งเวลา CPU
ในแง่ทางการ进程
คือหน่วยที่เล็กที่สุดของการจัดสรรทรัพยากร CPU
线程
线程
หน่วยที่รันโปรแกรมตาม进程
ในแง่ของคนทั่วไป线程
คือโฟลว์การดำเนินการใน进程
มีโฟลว์การดำเนินการเพียงขั้นตอนเดียวใน进程
ที่เรียกว่า单线程
นั่นคือเมื่อโปรแกรมถูกดำเนินการ เส้นทางของโปรแกรมจะถูกจัดเรียงตามลำดับตามลำดับ จะต้องดำเนินการก่อนหน้านี้ก่อนที่จะดำเนินการในภายหลัง
สตรีมการดำเนินการหลายรายการใน进程
เรียกว่า多线程
กล่าวคือ สามารถรัน线程
ที่แตกต่างกันหลายรายการพร้อมกันในโปรแกรมเพื่อทำงานที่แตกต่างกัน ซึ่งหมายความว่าโปรแกรมเดียวได้รับอนุญาตให้สร้าง线程
การดำเนินการแบบขนานหลายรายการเพื่อทำงานตามลำดับให้เสร็จสิ้น .
ผู้เขียนจะยกตัวอย่างง่ายๆ ด้านล่าง เช่น ถ้าเราเปิด qq音乐
แล้วฟังเพลง ก็สามารถเข้าใจ qq音乐
qq音乐
เป็นกระบวนการหนึ่งได้ ไปที่เพลงเป็นเธรด และการดาวน์โหลดเป็นกระบวนการ ถ้าเราเปิด vscode
อีกครั้งเพื่อเขียนโค้ดมันจะเป็นอีกกระบวนการหนึ่ง
กระบวนการเป็นอิสระจากกัน แต่ทรัพยากรบางอย่างถูกใช้ร่วมกันระหว่างเธรดภายใต้กระบวนการเดียวกัน
วงจรชีวิตของเธรดต้องผ่านห้าขั้นตอน
สถานะใหม่: หลังจากใช้คีย์เวิร์ด new
และคลาส Thread
หรือคลาสย่อยเพื่อสร้างวัตถุเธรด วัตถุเธรดจะอยู่ในสถานะใหม่ มันยังคงอยู่ในสถานะนี้จนกว่าโปรแกรม start()
เธรด
สถานะพร้อม: เมื่อวัตถุเธรดเรียกใช้เมธอด start()
เธรดจะเข้าสู่สถานะพร้อม เธรดในสถานะพร้อมอยู่ในคิวพร้อมและสามารถรันได้ทันทีตราบใดที่ได้รับสิทธิ์ในการใช้ CPU
สถานะกำลังทำงาน: หากเธรดอยู่ในสถานะพร้อมรับทรัพยากร CPU
เธรดนั้นสามารถดำเนินการได้ run()
และเธรดอยู่ในสถานะกำลังทำงาน เธรดที่อยู่ในสถานะกำลังทำงานนั้นซับซ้อนที่สุด อาจถูกบล็อก พร้อมใช้งานและไม่ทำงาน
สถานะการบล็อก: หากเธรดดำเนินการ sleep(睡眠)
, suspend(挂起)
, wait(等待)
และวิธีการอื่น ๆ หลังจากสูญเสียทรัพยากรที่ถูกครอบครอง เธรดจะเข้าสู่สถานะการบล็อกจากสถานะที่ทำงานอยู่ สถานะพร้อมสามารถกลับเข้ามาใหม่ได้หลังจากเวลาพักเครื่องหมดลงหรือได้รับทรัพยากรอุปกรณ์แล้ว สามารถแบ่งได้เป็นสามประเภท:
การรอบล็อก: เธรดในสถานะกำลังทำงานดำเนินการเมธอด wait()
ทำให้เธรดเข้าสู่สถานะการบล็อกการรอ
การบล็อกแบบซิงโครนัส: เธรดล้มเหลวในการรับการล็อคการซิงโครไนซ์ synchronized
ซ์ (เนื่องจากการล็อคการซิงโครไนซ์ถูกครอบครองโดยเธรดอื่น)
การบล็อกอื่น ๆ: เมื่อมีการร้องขอ I/O
โดยการเรียกเธรดของ sleep()
หรือ join()
เธรดจะเข้าสู่สถานะการบล็อก เมื่อสถานะ sleep()
หมดเวลา join()
จะรอให้เธรดยุติหรือหมดเวลา หรือการประมวลผล I/O
เสร็จสิ้น และเธรดจะกลับสู่สถานะพร้อม
สถานะการสิ้นสุด: เมื่อเธรดที่รันอยู่ทำงานเสร็จสิ้นหรือมีเงื่อนไขการสิ้นสุดอื่น ๆ เกิดขึ้น เธรดจะสลับไปยังสถานะที่สิ้นสุด
JS
ในฐานะภาษาสคริปต์ของเบราว์เซอร์ JS
ส่วนใหญ่จะใช้เพื่อโต้ตอบกับผู้ใช้และดำเนินการ DOM
สิ่งนี้กำหนดว่าสามารถเป็นแบบเธรดเดียวเท่านั้น มิฉะนั้นจะทำให้เกิดปัญหาการซิงโครไนซ์ที่ซับซ้อนมาก ตัวอย่างเช่น สมมติว่า JavaScript
มีสองเธรดในเวลาเดียวกัน เธรดหนึ่งเพิ่มเนื้อหาให้กับโหนด DOM
บางตัว และอีกเธรดหนึ่งลบโหนด ในกรณีนี้ เธรดใดที่เบราว์เซอร์ควรใช้
เมื่อเอ็นจิ้น JS
แยกวิเคราะห์ส่วนโค้ดที่ปฏิบัติการได้ (โดยปกติจะเป็นเฟสการเรียกใช้ฟังก์ชัน) อันดับแรกจะดำเนินการเตรียมการบางอย่างก่อนดำเนินการ "งานเตรียมการ" นี้เรียกว่า "บริบทการดำเนินการ " (บริบทการดำเนินการ (เรียกว่า EC
)" หรืออาจเรียกว่า สภาพแวดล้อมการดำเนินการ ก็ได้
มีบริบทการดำเนินการสามประเภทใน javascript
ซึ่งได้แก่:
บริบทการดำเนินการทั่วโลก นี่เป็นบริบทการดำเนินการเริ่มต้นหรือขั้นพื้นฐานที่สุด จะมีบริบทสากลเพียงบริบทเดียวในโปรแกรม และจะมีอยู่ตลอดวงจรชีวิตของ สคริปต์ javascript
ด้านล่างของสแต็กการดำเนินการจะไม่ถูกทำลายโดยการแตกสแต็ก บริบทส่วนกลางจะสร้างวัตถุส่วนกลาง (โดยใช้สภาพแวดล้อมของเบราว์เซอร์เป็นตัวอย่าง วัตถุส่วนกลางนี้คือ window
) และผูกค่า this
กับวัตถุส่วนกลางนี้
บริบทการดำเนินการฟังก์ชัน เมื่อใดก็ตามที่มีการเรียกใช้ฟังก์ชัน บริบทการดำเนินการฟังก์ชันใหม่จะถูกสร้างขึ้น (ไม่ว่าฟังก์ชันจะถูกเรียกใช้ซ้ำๆ หรือไม่ก็ตาม)
บริบทการดำเนินการฟังก์ชัน Eval โค้ดที่ดำเนินการภายในฟังก์ชัน eval
จะมีบริบทการดำเนินการของตัวเองด้วย แต่เนื่องจาก eval
ไม่ค่อยได้ใช้ จึงจะไม่ถูกวิเคราะห์ที่นี่
ก่อนหน้านี้เราได้กล่าวไว้ว่า js
จะสร้างบริบทการดำเนินการเมื่อมันทำงาน แต่บริบทการดำเนินการจำเป็นต้องถูกจัดเก็บ แล้วจะใช้ในการจัดเก็บอะไร? คุณต้องใช้โครงสร้างข้อมูลสแต็ก
สแต็กเป็นโครงสร้างข้อมูลที่เข้าก่อน-หลังออก
โดยสรุป บริบทการดำเนินการที่ใช้ในการจัดเก็บบริบทการดำเนินการที่สร้างขึ้นเมื่อโค้ดกำลังทำงานคือสแต็กการดำเนินการ
เมื่อดำเนินการโค้ดส่วนหนึ่ง JS
จากนั้นกลไก JS
จะสร้างบริบทการดำเนินการทั่วโลกและ push
ไปยังสแต็กการดำเนินการ ในกระบวนการนี้ กลไก JS
จะจัดสรรหน่วยความจำสำหรับตัวแปรทั้งหมดในโค้ดนี้และกำหนดค่าเริ่มต้น (ไม่ได้กำหนด) หลังจากการสร้างเสร็จสมบูรณ์ กลไก JS
จะเข้าสู่การดำเนินการ ในกระบวนการนี้ กลไก JS
จะดำเนินการโค้ดทีละบรรทัด นั่นคือกำหนดค่า (ค่าจริง) ให้กับตัวแปรที่ได้รับการจัดสรรหน่วยความจำทีละรายการ
หากมีการเรียก function
ในโค้ดนี้ กลไก JS
จะสร้างบริบทการดำเนินการฟังก์ชันและ push
ไปยังสแต็กการดำเนินการ การสร้างและการดำเนินการจะเหมือนกับบริบทการดำเนินการทั่วโลก
เมื่อสแต็กการดำเนินการเสร็จสมบูรณ์ บริบทการดำเนินการจะถูกดึงออกมาจากสแต็ก จากนั้นบริบทการดำเนินการถัดไปจะถูกป้อน
ผมขอยกตัวอย่างด้านล่าง หากเรามีโค้ดต่อไปนี้ในโปรแกรมของเรา:
console.log("Global Execution Context start"); ฟังก์ชั่นแรก () { console.log("ฟังก์ชั่นแรก"); ที่สอง(); console.log("ฟังก์ชั่นแรกอีกครั้ง"); - ฟังก์ชั่นวินาที () { console.log("ฟังก์ชั่นที่สอง"); - อันดับแรก(); console.log("Global Execution Context end");
มาวิเคราะห์ตัวอย่างข้างต้นกันก่อน
ขั้นแรก สแต็กการดำเนินการจะถูกสร้างขึ้น
จากนั้นบริบทส่วนกลางจะถูกสร้างขึ้น และบริบทการดำเนินการจะ push
ไปยังสแต็กการดำเนินการ
เพื่อเริ่มการดำเนินการ และ Global Execution Context start
พบวิธี first
ดำเนินการวิธีการ สร้างบริบทการดำเนินการฟังก์ชัน และ push
ไปยังสแต็กการดำเนินการ
เพื่อดำเนินการบริบทการดำเนินการ first
first function
พบวิธี second
ดำเนินการวิธีการ สร้างบริบทการดำเนินการของฟังก์ชันและ push
ไปที่สแต็กการดำเนินการเพื่อ
ดำเนินการบริบทการดำเนินการ second
second function
การดำเนินการ second
ได้รับการดำเนินการ ดึงข้อมูลจากสแต็ก และป้อนบริบทการดำเนินการ first
ไป บริบทการดำเนินการ
first
จะดำเนินต่อไป การดำเนินการ ส่งออก Again first function
บริบทการดำเนินการ first
ได้รับการดำเนินการ popped จากสแต็ก และป้อนบริบทการดำเนินการถัดไป บริบท
การดำเนินการทั่วโลก บริบท Global Execution Context end
เราใช้รูปภาพเพื่อสรุป
ใช้ได้. หลังจากพูดถึงบริบทการดำเนินการและสแต็กการดำเนินการแล้ว เรามาพูดถึงกลไกการดำเนินการของ js
กันดีกว่า
js
เข้าใจงานซิงโครนัส งานอะซิงโครนัส งานแมโคร และงานไมโครใน js
ใน js
งานจะถูกแบ่งออกเป็นงานซิงโครนัสและงานอะซิงโครนัส ดังนั้นงานซิงโครนัสคืออะไรและงานอะซิงโครนัสคืออะไร?
งานแบบซิงโครนัสหมายถึงงานที่จัดคิวไว้สำหรับการดำเนินการบนเธรดหลัก งานถัดไปสามารถดำเนินการได้หลังจากดำเนินการงานก่อนหน้าแล้วเท่านั้น
งานแบบอะซิงโครนัสหมายถึงงานที่ไม่ได้เข้าสู่เธรดหลัก แต่เข้าสู่ "คิวงาน" (งานในคิวงานจะถูกดำเนินการควบคู่ไปกับเธรดหลัก) เฉพาะเมื่อเธรดหลักไม่ได้ใช้งานและ "คิวงาน" จะแจ้งให้ทราบ เธรดหลัก ซึ่งเป็นงานอะซิงโครนัส เมื่อสามารถดำเนินการได้ งานจะเข้าสู่เธรดหลักเพื่อดำเนินการ เนื่องจากเป็นที่เก็บคิว จึงเป็นไปตามกฎเข้าก่อนออกก่อน งานอะซิงโครนัสทั่วไป ได้แก่ setInterval
, setTimeout
, promise.then
ฯลฯ
ได้แนะนำงานแบบซิงโครนัสและงานแบบอะซิงโครนัสแล้ว ตอนนี้เรามาพูดถึง Event Loop กันดีกว่า
งานแบบซิงโครนัสและแบบอะซิงโครนัสจะเข้าสู่ "สถานที่" การดำเนินการที่แตกต่างกันตามลำดับ และป้อนเธรดหลักพร้อมกันเมื่องานก่อนหน้าเสร็จสมบูรณ์เท่านั้นที่สามารถดำเนินการงานถัดไปได้ งานแบบอะซิงโครนัสไม่ได้เข้าสู่เธรดหลัก แต่เข้าสู่ Event Table
และฟังก์ชันการลงทะเบียน
เมื่อสิ่งที่ระบุเสร็จสิ้น Event Table
จะย้ายฟังก์ชันนี้ไปที่ Event Queue
Event Queue
เป็นโครงสร้างข้อมูลคิว ดังนั้นจึงเป็นไปตามกฎเข้าก่อนออกก่อน
เมื่องานในเธรดหลักว่างเปล่าหลังจากดำเนินการ ฟังก์ชันที่เกี่ยวข้องจะถูกอ่านจาก Event Queue
และดำเนินการในเธรดหลัก
กระบวนการข้างต้นจะทำซ้ำอย่างต่อเนื่อง ซึ่งมักเรียกว่า Event Loop
มาสรุปด้วยภาพกัน
ผมขอแนะนำ
ฟังก์ชันตัวอย่างสั้นๆ test1() { console.log("log1"); setTimeout(() => { console.log("setTimeout 1,000"); }, 1,000); setTimeout(() => { console.log("setTimeout 100"); }, 100); console.log("log2"); - test1(); // log1, log2, setTimeout 100, setTimeout 1000
เรารู้ว่าใน js งานแบบซิงโครนัสจะถูกดำเนินการก่อนงานแบบอะซิงโครนัส ดังนั้นตัวอย่างข้างต้นจะแสดงผลลัพธ์ log1、log2
ก่อน
จากนั้นจึงรันงานแบบอะซิงโครนัสหลังจากซิงโครนัส
ดังนั้น
100
setTimeout 100
โทรกลับที่มีความล่าช้า 1000
มิลลิวินาที จะดำเนินการเอาต์พุต setTimeout 1000
ในภายหลัง
ตราบใดที่คุณเข้าใจงานซิงโครนัสและอะซิงโครนัสที่ผู้เขียนกล่าวถึงข้างต้นก็จะไม่มีปัญหา งั้นผมขอยกตัวอย่างอีกอันนะครับ เพื่อนๆ มาดูกันว่าผลลัพธ์จะเป็นอย่างไร
ฟังก์ชั่น test2() { console.log("log1"); setTimeout(() => { console.log("setTimeout 1,000"); }, 1,000); setTimeout(() => { console.log("setTimeout 100"); }, 100); สัญญาใหม่ ((แก้ไข, ปฏิเสธ) => { console.log("สัญญาใหม่"); แก้ไข (); }).แล้ว(() => { console.log("สัญญาแล้ว"); - console.log("log2"); - test2();
เพื่อแก้ปัญหาข้างต้น การรู้งานแบบซิงโครนัสและแบบอะซิงโครนัสนั้นไม่เพียงพอ เรายังจำเป็นต้องรู้งานแมโครและงานย่อยด้วย
ใน js
งานแบ่งออกเป็นสองประเภท งานหนึ่งเรียกว่างานมาโคร MacroTask
และอีกงานเรียกว่างานไมโคร MicroTask
งานแมโครทั่วไป MacroTask
มี
บล็อกโค้ดหลัก
setTimeout()
setInterval()
setImmediate() - Node
requestAnimationFrame() - เบราว์เซอร์
งานไมโครทั่วไป MicroTask
มี
Promise.then()
process.nextTick() -
Node ตัวอย่างข้างต้นเกี่ยวข้องกับงานแมโครและงานไมโคร ลำดับการดำเนินการของงานแมโครและงานไมโครคืออะไร?
ก่อนอื่น เมื่อ script
โดยรวม (เป็นงานมาโครแรก) เริ่มดำเนินการ โค้ดทั้งหมดจะถูกแบ่งออกเป็นสองส่วน: งานซิงโครนัส และงานอะซิงโครนัสจะเข้าสู่เธรดหลักโดยตรงสำหรับการดำเนินการตามลำดับ และงานอะซิงโครนัสจะเข้าสู่คิวอะซิงโครนัสแล้วแบ่งออกเป็นงานมาโครและงานย่อย
งานแมโครจะเข้าสู่ Event Table
และลงทะเบียนฟังก์ชันการโทรกลับในนั้น เมื่อใดก็ตามที่เหตุการณ์ที่ระบุเสร็จสิ้น Event Table
จะย้าย Event Queue
นี้
ไปยังคิว Event Table
ด้วย เมื่อใดก็ตามที่กิจกรรมที่ระบุเสร็จสิ้น Event Table
จะย้ายฟังก์ชันนี้ไปยัง Event Queue
เมื่องานในเธรดหลักเสร็จสิ้นและเธรดหลักว่างเปล่า Event Queue
ของไมโครทาสก์จะถูกตรวจสอบ หากมีงานอยู่ , Execute ทั้งหมด, ถ้าไม่ใช่, รันงานแมโครถัดไป
เราใช้รูปภาพเพื่อสรุป
หลังจากทำความเข้าใจตัวอย่างข้างต้นของงานแมโครและงานย่อยแบบอะซิงโครนัสแล้ว เราก็จะได้รับคำตอบได้อย่างง่ายดาย
เรารู้ว่าใน js งานซิงโครนัสจะถูกดำเนินการก่อนงานอะซิงโครนัส ดังนั้นตัวอย่างข้างต้นจะแสดงผลลัพธ์ log1、new promise、log2
ก่อน ควรสังเกตไว้ที่นี่ว่าบล็อกโค้ดหลัก ของสัญญาใหม่ได้รับการซิง
โครไนซ์ หลังจากดำเนินการงานแมโครแล้ว งานไมโครทั้งหมดที่สร้างโดยงานแมโครนี้จะถูกดำเนินการ ดังนั้น promise.then
จะถูกส่งออก
หลังจากงานไมโครทั้งหมดถูกดำเนินการ งานแมโครอื่นจะถูกดำเนินการ ทำให้ล่าช้า ฟังก์ชันการเรียกกลับ 100
มิลลิวินาทีจะจัดลำดับความสำคัญของการดำเนินการและเอาต์พุต setTimeout 100
งานแมโครนี้ไม่สร้างงานไมโครทาสก์ ดังนั้นจึงไม่มีงานไมโครทาสก์ที่จำเป็นต้องดำเนินการ
เพื่อดำเนินการงานเรียกกลับครั้งถัดไป
ฟัง
ก์ชั่นที่มีความล่าช้า 1000
จะจัดลำดับความสำคัญของการดำเนินการและเอาต์พุต setTimeout 1000
ดังนั้นหลังจากดำเนินการวิธี test2 แล้ว เอาต์พุต log1、new promise、log2、promise.then、setTimeout 100、setTimeout 1000
ตามลำดับ
ความคิดเห็นที่แตกต่างกันว่าจะรัน
js
ก่อนด้วยงานมาโคร แล้วตามด้วยงานไมโคร หรือกับงานไมโครก่อนงานมาโคร ความเข้าใจของผู้เขียนคือว่าหากบล็อกโค้ดjs
ทั้งหมดถือเป็นงานมาโคร ลำดับการดำเนินการjs
ของเราจะเป็นงานมาโครก่อนแล้วจึงเป็นงานขนาดเล็ก
ดังคำกล่าวที่ว่า การฝึกฝนเพียงครั้งเดียวย่อมดีกว่าการดูเป็นร้อยครั้ง ฉันจะยกตัวอย่างให้คุณดู 2 ตัวอย่างด้านล่างนี้ หากคุณสามารถทำได้อย่างถูกต้อง แสดงว่าคุณได้เชี่ยวชาญความรู้เกี่ยวกับกลไกการดำเนินการ js
แล้ว
ตัวอย่างที่ 1
ฟังก์ชั่น test3() { คอนโซล.บันทึก(1); setTimeout (ฟังก์ชัน () { คอนโซล.บันทึก(2); สัญญาใหม่ (ฟังก์ชั่น (แก้ไข) { console.log(3); แก้ไข (); }).แล้ว(ฟังก์ชัน () { คอนโซล.บันทึก(4); - คอนโซล.บันทึก(5); }, 1,000); สัญญาใหม่ (ฟังก์ชั่น (แก้ไข) { คอนโซล.บันทึก(6); แก้ไข (); }).แล้ว(ฟังก์ชัน () { คอนโซล.บันทึก(7); setTimeout (ฟังก์ชัน () { คอนโซล.บันทึก(8); - - setTimeout (ฟังก์ชัน () { คอนโซล.บันทึก(9); สัญญาใหม่ (ฟังก์ชั่น (แก้ไข) { console.log(10); แก้ไข (); }).แล้ว(ฟังก์ชัน () { console.log(11); - }, 100); console.log(12); - test3();
มาวิเคราะห์กันโดยละเอียด
ขั้นแรก บล็อกโค้ด js
โดยรวมจะถูกดำเนินการเป็นงานมาโคร และ 1, 1、6、12
จะถูกส่งออกตามลำดับ
หลังจากดำเนินการงานแมโครบล็อคโค้ดโดยรวมแล้ว งานไมโครหนึ่งงานและงานแมโครสองงานจะถูกสร้างขึ้น ดังนั้นคิวงานแมโครจึงมีงานแมโครสองงาน และคิวงานไมโครมีงานไมโครหนึ่งงาน
หลังจากดำเนินการงานแมโคร งานย่อยทั้งหมดที่สร้างโดยงานแมโครนี้จะถูกดำเนินการ เนื่องจากมีไมโครทาสก์เพียงอันเดียว 7
จะถูกส่งออก ไมโครทาสก์นี้สร้างมาโครทาสก์อีกตัว ดังนั้นขณะนี้มีสามมาโครทาสก์ในคิวแมโครทาสก์
ในบรรดาแมโครทาสก์ทั้งสามนั้น แมโครทาสก์ที่ไม่มีการตั้งค่าการหน่วงเวลาจะถูกดำเนินการก่อน ดังนั้น 8
จึงเป็นเอาต์พุต จึงไม่สร้างไมโครทาสก์ ดังนั้นจึงไม่มีไมโครทาสก์ที่จะดำเนินการ และแมโครทาสก์ถัดไปยังคงถูกดำเนินการต่อไป
ชะลอการดำเนินการของแมโครทาสก์เป็นเวลา 100
มิลลิวินาที เอาต์พุต 9、10
และสร้างไมโครทาสก์ ดังนั้นคิวไมโครทาสก์จึงมีไมโครทาสก์ในขณะนี้
หลังจากดำเนินการแมโครทาสก์แล้ว ไมโครทาสก์ทั้งหมดที่สร้างโดยแมโครทาสก์จะถูกดำเนินการ ดังนั้นไมโครทาสก์จะถูกดำเนินการ
แล้ว
ไมโครทาสก์ทั้งหมดในเอาต์พุตคิวงาน 11
การดำเนินการแมโครทาสก์จะส่งออกเอาต์พุต 2、3、5
โดยมีความล่าช้า 1000
มิลลิวินาที และไมโครทาสก์จึงถูกสร้างขึ้น
Macrotask จะถูกดำเนินการ microtask ทั้งหมดจะถูกสร้างขึ้น ดังนั้น microtask ทั้งหมดในคิว microtask จะถูกดำเนินการ และ 4
จะถูกส่งออก
ดังนั้นตัวอย่างโค้ดข้างต้นจะส่งออก 1、6、12、7、8、9、10、11、2、3、5、4
, 12, 7, 8, 9, 10, 11 , 2, 3, 5, 4 ตามลำดับ พวกคุณทำถูกแล้วใช่ไหม?
ตัวอย่างที่ 2:
เราแก้ไขตัวอย่างข้างต้นเล็กน้อย 1 และแนะนำ async
และ await
ฟังก์ชัน async test4() { คอนโซล.บันทึก(1); setTimeout (ฟังก์ชัน () { คอนโซล.บันทึก(2); สัญญาใหม่ (ฟังก์ชั่น (แก้ไข) { console.log(3); แก้ไข (); }).แล้ว(ฟังก์ชัน () { คอนโซล.บันทึก(4); - คอนโซล.บันทึก(5); }, 1,000); สัญญาใหม่ (ฟังก์ชั่น (แก้ไข) { คอนโซล.บันทึก(6); แก้ไข (); }).แล้ว(ฟังก์ชัน () { คอนโซล.บันทึก(7); setTimeout (ฟังก์ชัน () { คอนโซล.บันทึก(8); - - ผลลัพธ์ const = รอ async1(); console.log(ผลลัพธ์); setTimeout (ฟังก์ชัน () { คอนโซล.บันทึก(9); สัญญาใหม่ (ฟังก์ชั่น (แก้ไข) { console.log(10); แก้ไข (); }).แล้ว(ฟังก์ชัน () { console.log(11); - }, 100); console.log(12); - ฟังก์ชันอะซิงก์ async1() { console.log(13) return Promise.resolve("สัญญาแก้ไข"); - test4();
ตัวอย่างข้างต้นจะแสดงผลอะไร? ที่นี่เราสามารถแก้ปัญหา async
และ await
ได้อย่างง่ายดาย
เรารู้ async
และ await
นั้นเป็นไวยากรณ์ของ Promise
จริงๆ ที่นี่เราแค่ต้องรู้ await
นั้นเทียบเท่ากับ Promise.then
ดังนั้นเราจึงสามารถเข้าใจตัวอย่างข้างต้นเป็น
ฟังก์ชันโค้ดต่อไปนี้ test4() { คอนโซล.บันทึก(1); setTimeout (ฟังก์ชัน () { คอนโซล.บันทึก(2); สัญญาใหม่ (ฟังก์ชั่น (แก้ไข) { console.log(3); แก้ไข (); }).แล้ว(ฟังก์ชัน () { คอนโซล.บันทึก(4); - คอนโซล.บันทึก(5); }, 1,000); สัญญาใหม่ (ฟังก์ชั่น (แก้ไข) { คอนโซล.บันทึก(6); แก้ไข (); }).แล้ว(ฟังก์ชัน () { คอนโซล.บันทึก(7); setTimeout (ฟังก์ชัน () { คอนโซล.บันทึก(8); - - สัญญาใหม่ (ฟังก์ชั่น (แก้ไข) { console.log(13); กลับแก้ไข ("Promise.resolve"); }).แล้ว((ผลลัพธ์) => { console.log(ผลลัพธ์); setTimeout (ฟังก์ชัน () { คอนโซล.บันทึก(9); สัญญาใหม่ (ฟังก์ชั่น (แก้ไข) { console.log(10); แก้ไข (); }).แล้ว(ฟังก์ชัน () { console.log(11); - }, 100); console.log(12); - - test4();
คุณสามารถรับผลลัพธ์ได้อย่างง่ายดายหลังจากเห็นโค้ดด้านบนหรือไม่?
ขั้นแรก บล็อกโค้ด js
ทั้งหมดจะถูกดำเนินการเป็นงานมาโครในขั้นต้น และเอาต์พุต 1、6、13
ตามลำดับ
หลังจากดำเนินการงานแมโครบล็อคโค้ดโดยรวมแล้ว งานไมโครสองงานและงานแมโครหนึ่งงานจะถูกสร้างขึ้น ดังนั้นคิวงานแมโครจึงมีงานแมโครหนึ่งงาน และคิวงานไมโครมีสองงานไมโคร
หลังจากดำเนินการงานแมโคร งานย่อยทั้งหมดที่สร้างโดยงานแมโครนี้จะถูกดำเนินการ ดังนั้น 7、Promise.resolve、12
จะถูกส่งออก ไมโครทาสก์นี้สร้างมาโครทาสก์เพิ่มอีกสองงาน ดังนั้นคิวงานมาโครทาสก์ในปัจจุบันจึงมีมาโครงานสามชิ้น
ในบรรดาแมโครทาสก์ทั้งสามนั้น แมโครทาสก์ที่ไม่มีการตั้งค่าการหน่วงเวลาจะถูกดำเนินการก่อน ดังนั้น 8
จึงเป็นเอาต์พุต จึงไม่สร้างไมโครทาสก์ ดังนั้นจึงไม่มีไมโครทาสก์ที่จะดำเนินการ และแมโครทาสก์ถัดไปยังคงถูกดำเนินการต่อไป
ชะลอการดำเนินการของแมโครทาสก์เป็นเวลา 100
มิลลิวินาที เอาต์พุต 9、10
และสร้างไมโครทาสก์ ดังนั้นคิวไมโครทาสก์จึงมีไมโครทาสก์ในขณะนี้
หลังจากดำเนินการแมโครทาสก์แล้ว ไมโครทาสก์ทั้งหมดที่สร้างโดยแมโครทาสก์จะถูกดำเนินการ ดังนั้นไมโครทาสก์จะถูกดำเนินการ
แล้ว
ไมโครทาสก์ทั้งหมดในเอาต์พุตคิวงาน 11
การดำเนินการแมโครทาสก์จะส่งออกเอาต์พุต 2、3、5
โดยมีความล่าช้า 1000
มิลลิวินาที และไมโครทาสก์จึงถูกสร้างขึ้น
Macrotask จะถูกดำเนินการ microtask ทั้งหมดที่สร้างขึ้นจะดำเนินการ microtask ทั้งหมดในคิว microtask และเอาต์พุต 4
ดังนั้นตัวอย่างโค้ดด้านบนจะส่งออก 1, 6, 13, 7 1、6、13、7、Promise.resolve、12、8、9、10、11、2、3、5、4
4 พวกคุณทำถูกหรือเปล่า?
เพื่อนหลายคนอาจยังไม่เข้าใจ setTimeout(fn)
เห็นได้ชัดว่าไม่ได้ตั้งเวลาหน่วงไว้ใช่ไหม
เราสามารถเข้าใจ setTimeout(fn)
เป็น setTimeout(fn,0)
ซึ่งจริงๆ แล้วหมายถึงสิ่งเดียวกัน
เรารู้ว่า js แบ่งออกเป็นงานแบบซิงโครนัสและงานแบบอะซิงโครนัส setTimeout(fn)
เป็นงานแบบอะซิงโครนัส ดังนั้นแม้ว่าคุณจะไม่ได้ตั้งเวลาหน่วงไว้ที่นี่ งานก็จะเข้าสู่คิวแบบอะซิงโครนัสและจะไม่ถูกดำเนินการจนกว่าเธรดหลักจะถูกดำเนินการ ไม่ได้ใช้งาน
ผู้เขียนจะพูดถึงมันอีกครั้ง คุณคิดว่าเวลาหน่วงที่เราตั้งไว้หลังจาก setTimeout
, js
จะถูกดำเนินการตามเวลาล่าช้าของเราอย่างแน่นอน ฉันไม่คิดอย่างนั้น เวลาที่เราตั้งไว้คือเพียงว่าสามารถดำเนินการฟังก์ชันการโทรกลับได้ แต่เธรดหลักจะว่างหรือไม่นั้นเป็นอีกเรื่องหนึ่ง
ฟังก์ชั่น test5() { setTimeout (ฟังก์ชัน () { console.log("setTimeout"); }, 100); ให้ฉัน = 0; ในขณะที่ (จริง) { ฉัน++; - - test5();
ตัวอย่างข้างต้นจะส่งออก setTimeout
หลังจาก 100
มิลลิวินาทีหรือไม่ ไม่ เนื่องจากเธรดหลักของเราได้เข้าสู่วงวนไม่สิ้นสุดและไม่มีเวลาดำเนินการงานคิวแบบอะซิงโครนัส
GUI渲染
การกล่าวถึงที่นี่ เพื่อนบางคนอาจไม่เข้าใจ ฉันจะแนะนำโดยละเอียดในบทความเกี่ยวกับเบราว์เซอร์ในภายหลัง นี่เป็นเพียงความเข้าใจสั้น ๆ
เนื่องจาก JS引擎线程
และ GUI渲染线程
เป็นแบบพิเศษร่วมกัน เพื่อเปิดใช้宏任务
และ DOM任务
เพื่อดำเนินการในลักษณะที่เป็นระเบียบ เบราว์เซอร์จะเริ่ม GUI渲染线程
หลังจากผลการดำเนินการของ宏任务
หนึ่งงานและก่อน การดำเนินการของ宏任务
ถัดไป แสดงผลเพจ
ดังนั้น ความสัมพันธ์ระหว่างงานมาโคร งานไมโคร และการเรนเดอร์ GUI จึงเป็นดังนี้
งานมาโคร -> งานไมโคร -> การเรนเดอร์ GUI -> งานมาโคร ->...
[คำแนะนำวิดีโอการสอนที่เกี่ยวข้อง: ส่วนหน้าของเว็บ]
ข้างต้นคือ การวิเคราะห์เชิงลึกของ JavaScript สำหรับรายละเอียดเกี่ยวกับบริบทการดำเนินการและกลไกการดำเนินการ โปรดอ่านบทความอื่น ๆ ที่เกี่ยวข้องบนเว็บไซต์ PHP Chinese สำหรับข้อมูลเพิ่มเติม!