
วิธีเริ่มต้นใช้งาน 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 สำหรับข้อมูลเพิ่มเติม!
