การดีบัก JavaScript ที่ไม่พร้อมกันด้วยเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome

เกริ่นนำ

ฟีเจอร์ทรงพลังที่ทำให้ JavaScript ไม่เหมือนใครคือความสามารถในการทำงานแบบไม่พร้อมกันผ่านฟังก์ชันเรียกกลับ การกำหนดโค้ดเรียกกลับแบบไม่พร้อมกันช่วยให้คุณเขียนโค้ดที่ขับเคลื่อนด้วยเหตุการณ์ได้ แต่ยังช่วยให้ติดตามข้อบกพร่องด้วยเนื่องจาก JavaScript ไม่ทำงานในลักษณะเชิงเส้น

โชคดีที่ตอนนี้ใน Chrome DevTools คุณสามารถดูชุดการเรียกกลับของ JavaScript แบบอะซิงโครนัสทั้งหมด!

ภาพรวมทีเซอร์สั้นๆ ของสแต็กการเรียกใช้แบบไม่พร้อมกัน
ภาพรวมทีเซอร์สั้นๆ ของสแต็กการเรียกใช้แบบไม่พร้อมกัน (เราจะแจกแจงขั้นตอนการสาธิตนี้ในเร็วๆ นี้)

เมื่อเปิดใช้ฟีเจอร์การเรียกใช้สแต็กแบบไม่พร้อมกันในเครื่องมือสำหรับนักพัฒนาเว็บแล้ว คุณจะเจาะลึกสถานะของเว็บแอปได้ ณ เวลาต่างๆ เดินหน้าสแต็กเทรซเต็มรูปแบบสำหรับ Listener เหตุการณ์บางรายการ, setInterval,setTimeout, XMLHttpRequest, สัญญา, requestAnimationFrame, MutationObservers และอื่นๆ

ระหว่างการเดินสแต็กเทรซ คุณยังวิเคราะห์ค่าของตัวแปร ณ จุดใดจุดหนึ่งของการดำเนินการรันไทม์ได้ด้วย เป็นเหมือนเครื่องย้อนเวลา สำหรับการแสดงสีหน้า

ลองมาเปิดใช้ฟีเจอร์นี้และดูสถานการณ์ตัวอย่างเหล่านี้กัน

เปิดใช้การแก้ไขข้อบกพร่องแบบไม่พร้อมกันใน Chrome

ลองใช้ฟีเจอร์ใหม่นี้โดยเปิดใช้ใน Chrome ไปที่แผงแหล่งที่มาของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome Canary

ถัดจากแผงกลุ่มการโทรทางด้านขวามือจะมีช่องทำเครื่องหมายใหม่สำหรับ "อะซิงโครนัส" สลับช่องทำเครื่องหมายเพื่อเปิดหรือปิดการแก้ไขข้อบกพร่องแบบไม่พร้อมกัน (แม้ว่าจะเปิดอยู่ คุณอาจไม่ต้องการปิด)

สลับฟีเจอร์อะซิงโครนัสเป็นเปิดหรือปิด

บันทึกเหตุการณ์ตัวจับเวลาที่ล่าช้าและการตอบสนอง XHR

ใน Gmail คุณอาจเห็นสิ่งนี้มาก่อนแล้ว

Gmail กำลังพยายามส่งอีเมลอีกครั้ง

หากเกิดปัญหาในการส่งคำขอ (เซิร์ฟเวอร์มีปัญหาหรือมีปัญหาในการเชื่อมต่อเครือข่ายในฝั่งไคลเอ็นต์) Gmail จะพยายามส่งข้อความอีกครั้งโดยอัตโนมัติหลังจากหมดเวลาสั้นๆ

หากต้องการดูว่าสแต็กการเรียกใช้แบบไม่พร้อมกันช่วยเราวิเคราะห์เหตุการณ์ตัวจับเวลาที่ล่าช้าและการตอบสนอง XHR ได้อย่างไร เราจึงได้สร้างขั้นตอนนั้นขึ้นมาอีกครั้งโดยมีตัวอย่าง Gmail จำลอง ดูโค้ด JavaScript แบบเต็มได้ในลิงก์ด้านบน แต่ขั้นตอนมีดังนี้

ผังงานของตัวอย่าง Gmail จำลอง
ในแผนภาพด้านบน เมธอดที่ไฮไลต์ด้วยสีฟ้าเป็นจุดหลักที่ทำให้ฟีเจอร์ DevTool ใหม่นี้เป็นประโยชน์มากที่สุดเนื่องจากวิธีการเหล่านี้ทำงานแบบไม่พร้อมกัน

การดูที่แผงการเรียกใช้ในเครื่องมือสำหรับนักพัฒนาเว็บเวอร์ชันก่อนหน้าเพียงอย่างเดียวแล้ว เบรกพอยท์ภายใน postOnFail() จะทำให้คุณได้ข้อมูลเพียงเล็กน้อยว่า postOnFail() ถูกเรียกมาจากที่ใด แต่ให้ดูข้อแตกต่างเมื่อ เปิดสแต็กที่ไม่พร้อมกัน

ก่อน
ตั้งค่าเบรกพอยท์ในตัวอย่าง Gmail จำลองโดยไม่มีสแต็กการเรียกใช้แบบไม่พร้อมกัน
แผงการเรียกใช้สแต็กโดยไม่เปิดใช้อะซิงโครนัส

คุณจะเห็นว่า postOnFail() เริ่มต้นจากการติดต่อกลับ AJAX แต่ไม่มีข้อมูลเพิ่มเติม

หลัง
ชุดเบรกพอยท์ในตัวอย่าง Gmail จำลองที่มีสแต็กการเรียกใช้แบบไม่พร้อมกัน
แผงการเรียกใช้สแต็กที่เปิดใช้อะซิงโครนัส

จากตรงนี้คุณจะเห็นว่า XHR เริ่มต้นมาจาก submitHandler() เยี่ยมไปเลย

เมื่อเปิดใช้สแต็กการเรียกใช้แบบไม่พร้อมกัน คุณจะดูสแต็กการเรียกใช้ทั้งหมดได้อย่างง่ายดายเพื่อดูว่าคำขอเริ่มต้นจาก submitHandler() (ซึ่งเกิดขึ้นหลังจากที่คลิกปุ่มส่ง) หรือจาก retrySubmit() (ซึ่งเกิดขึ้นหลังจากความล่าช้า setTimeout()) โดยทำดังนี้

submitHandler()
ชุดเบรกพอยท์ในตัวอย่าง Gmail จำลองที่มีสแต็กการเรียกใช้แบบไม่พร้อมกัน
retrySubmit()
ชุดเบรกพอยท์อีกชุดหนึ่งในตัวอย่างจำลองของ Gmail ที่มีสแต็กการเรียกใช้แบบไม่พร้อมกัน

ดูนิพจน์แบบไม่พร้อมกัน

เมื่อคุณเดินหน้าการเรียกใช้ทั้งสแต็ก นิพจน์ที่ดูจะอัปเดตด้วยเพื่อแสดงถึงสถานะ ณ ขณะนั้น

ตัวอย่างการใช้นิพจน์การดูกับสแต็กการเรียกใช้ aysnc

ประเมินโค้ดจากขอบเขตที่ผ่านมา

นอกจากนิพจน์การดูแล้ว คุณยังโต้ตอบกับโค้ดจากขอบเขตก่อนหน้าได้ในแผงคอนโซล JavaScript ของ DevTools

ลองจินตนาการว่าคุณคือ Dr. Who และต้องการความช่วยเหลือเล็กน้อยในการเปรียบเทียบนาฬิกาก่อนที่คุณจะไปถึง Tardis กับ "ตอนนี้" คุณสามารถประเมิน จัดเก็บ และคำนวณค่าจากจุดปฏิบัติการต่างๆ ได้อย่างง่ายดายจากคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บ

ตัวอย่างการใช้คอนโซล JavaScript กับสแต็กการเรียกใช้ aysnc
ใช้คอนโซล JavaScript ร่วมกับสแต็กการเรียกใช้แบบไม่พร้อมกันเพื่อแก้ไขข้อบกพร่องของโค้ด ดูการสาธิตได้ที่นี่

การใช้เครื่องมือสำหรับนักพัฒนาเว็บเพื่อจัดการกับนิพจน์จะช่วยให้คุณไม่ต้องเปลี่ยนกลับไปเป็นซอร์สโค้ด ทำการแก้ไข และรีเฟรชเบราว์เซอร์

คลี่คลายปณิธานที่สัญญาไว้แบบผูกมัด

หากคิดว่าขั้นตอนจำลองของ Gmail ก่อนหน้านี้ทำได้ยากหากไม่ได้เปิดใช้ฟีเจอร์สแต็กการเรียกใช้แบบไม่พร้อมกัน คุณนึกออกเลยว่าขั้นตอนอะซิงโครนัสที่ซับซ้อนมากขึ้น เช่น สัญญาแบบเชน จะยากขึ้นเพียงใด เรามาทบทวนตัวอย่างสุดท้ายของบทแนะนำของ Jake Archibald เกี่ยวกับพร็อพเพอร์ตี้ JavaScript กัน

นี่เป็นภาพเคลื่อนไหวสั้นๆ ของการเดินสแต็กการเรียกใช้ในตัวอย่าง async-best-example.html ของเจค

ก่อน
ตั้งค่าเบรกพอยท์ในตัวอย่างคำสัญญาโดยไม่มีสแต็กการเรียกใช้แบบไม่พร้อมกัน
แผงการเรียกใช้สแต็กโดยไม่เปิดใช้อะซิงโครนัส

สังเกตดูว่าแผงสแต็กการเรียกใช้มีข้อมูลที่ค่อนข้างสั้นเมื่อพยายามแก้ไขข้อบกพร่องของสัญญา

หลัง
ชุดเบรกพอยท์ในตัวอย่างสัญญากับสแต็กการเรียกใช้แบบไม่พร้อมกัน
แผงการเรียกใช้สแต็กที่เปิดใช้อะซิงโครนัส

ว้าว คำสัญญาอย่างนั้น การติดต่อกลับที่คุ้มค่ามาก

รับข้อมูลเชิงลึกเกี่ยวกับภาพเคลื่อนไหวบนเว็บของคุณ

ลองมาดูรายละเอียดเพิ่มเติมเกี่ยวกับที่เก็บถาวรของ HTML5Rocks จำภาพเคลื่อนไหวที่เร็วขึ้นด้วย requestAnimationFrame ของ Paul Lewis ได้ไหม

เปิดการสาธิต requestAnimationFrame และเพิ่มเบรกพอยท์ที่จุดเริ่มต้นของเมธอด update() (ประมาณบรรทัดที่ 874) ของ post.html การเรียกใช้สแต็กการเรียกใช้แบบไม่พร้อมกันทำให้เราได้รับข้อมูลเชิงลึกมากมายเกี่ยวกับ requestAnimationFrame รวมถึงสามารถกลับไปที่การเรียกกลับของเหตุการณ์การเลื่อนที่เริ่มต้นได้

ก่อน
มีการตั้งค่าเบรกพอยท์ในตัวอย่าง requestAnimationFrame โดยไม่มีสแต็กการเรียกใช้แบบไม่พร้อมกัน
แผงการเรียกใช้สแต็กโดยไม่เปิดใช้อะซิงโครนัส
หลัง
ชุดเบรกพอยท์ในตัวอย่าง requestAnimationFrame กับสแต็กการเรียกใช้แบบไม่พร้อมกัน
และเปิดใช้การทำงานไม่พร้อมกัน

ติดตามการอัปเดต DOM เมื่อใช้ MutationObserver

MutationObserver ช่วยให้เราสังเกตการเปลี่ยนแปลงใน DOM ได้ ในตัวอย่างแบบง่ายนี้ เมื่อคุณคลิกปุ่ม โหนด DOM ใหม่จะถูกเพิ่มต่อท้าย <div class="rows"></div>

เพิ่มเบรกพอยท์ภายใน nodeAdded() (บรรทัดที่ 31) ใน Demo.html เมื่อเปิดใช้สแต็กการเรียกใช้แบบไม่พร้อมกัน คุณจะย้ายสแต็กการเรียกใช้กลับไปยัง addNode() ไปยังเหตุการณ์การคลิกเริ่มต้นได้

ก่อน
ตั้งค่าเบรกพอยท์ในตัวอย่าง mutationObserver โดยไม่มีสแต็กการเรียกใช้แบบไม่พร้อมกัน
แผงการเรียกใช้สแต็กโดยไม่เปิดใช้อะซิงโครนัส
หลัง
ตั้งค่าเบรกพอยท์ในตัวอย่าง mutationObserver กับสแต็กการเรียกใช้แบบไม่พร้อมกัน
และเปิดใช้การทำงานไม่พร้อมกัน

เคล็ดลับในการแก้ไขข้อบกพร่องของ JavaScript ในสแต็กการเรียกใช้ที่ไม่พร้อมกัน

ตั้งชื่อฟังก์ชัน

หากคุณมีแนวโน้มที่จะกำหนดโค้ดเรียกกลับทั้งหมดเป็นฟังก์ชันที่ไม่ระบุชื่อ คุณอาจตั้งชื่อแทนเพื่อให้ดูสแต็กการเรียกใช้ได้ง่ายขึ้น

ตัวอย่างเช่น ใช้ฟังก์ชันที่ไม่ระบุชื่อดังต่อไปนี้

window.addEventListener('load', function() {
  // do something
});

แล้วตั้งชื่อ เช่น windowLoaded()

window.addEventListener('load', function <strong>windowLoaded</strong>(){
  // do something
});

เมื่อเหตุการณ์การโหลดเริ่มทำงาน เหตุการณ์ดังกล่าวจะแสดงในสแต็กเทรซของ DevTools โดยใช้ชื่อฟังก์ชันแทน "(ฟังก์ชันที่ไม่ระบุชื่อ)" ที่ไม่ชัดเจน ซึ่งช่วยให้คุณดูสิ่งที่กำลังเกิดขึ้นในสแต็กเทรซได้ง่ายขึ้น

ก่อน
ฟังก์ชันที่ไม่ระบุชื่อ
หลัง
ฟังก์ชันที่มีชื่อ

สำรวจเพิ่มเติม

กล่าวโดยสรุปก็คือ โค้ดเรียกกลับแบบไม่พร้อมกันทั้งหมดเหล่านี้ซึ่งเครื่องมือสำหรับนักพัฒนาเว็บจะแสดงการเรียกใช้สแต็กทั้งสาย

  • ตัวจับเวลา: เดินกลับไปยังจุดเริ่มต้นของ setTimeout() หรือ setInterval()
  • XHR: เดินกลับไปยังจุดที่เรียก xhr.send()
  • เฟรมภาพเคลื่อนไหว: เดินกลับไปยังจุดที่เรียก requestAnimationFrame
  • คำมั่นสัญญา: กลับไปยังข้อกล่าวหาที่ได้รับการแก้ไขแล้ว
  • Object.observe: เดินกลับไปยังบริเวณที่โค้ดเรียกกลับของผู้สังเกตการณ์ผูกไว้อยู่แต่เดิม
  • MutationObservers: เดินกลับไปยังจุดที่เกิดเหตุการณ์ผู้สังเกตการณ์การเปลี่ยนแปลง
  • window.postMessage(): ตรวจสอบการเรียกใช้การรับส่งข้อความภายในกระบวนการ
  • DataTransferItem.getAsString()
  • API ระบบไฟล์
  • IndexedDB
  • WebSQL
  • เหตุการณ์ DOM ที่มีสิทธิ์ผ่าน addEventListener(): กลับไปยังตำแหน่งที่เหตุการณ์เริ่มทำงาน เหตุการณ์ DOM บางรายการไม่มีสิทธิ์ใช้ฟีเจอร์สแต็กการเรียกใช้แบบไม่พร้อมกันเนื่องจากเหตุผลด้านประสิทธิภาพ ตัวอย่างเหตุการณ์ที่มีอยู่ในปัจจุบัน ได้แก่ "scroll", "hashchange" และ "selectionchange"
  • เหตุการณ์มัลติมีเดียผ่าน addEventListener(): กลับไปยังตำแหน่งที่เหตุการณ์เริ่มทำงาน เหตุการณ์มัลติมีเดียที่ใช้ได้ ได้แก่ เหตุการณ์เสียงและวิดีโอ (เช่น "เล่น" "หยุดชั่วคราว" "เปลี่ยนอัตรา") เหตุการณ์ WebRTC MediaStreamTrackList (เช่น "addtrack", "removetrack") และเหตุการณ์ MediaSource (เช่น "sourceopen")

การสามารถดูสแต็กเทรซของโค้ดเรียกกลับ JavaScript ได้ควรจะทำให้ขนเหล่านั้นติดอยู่บนศีรษะของคุณ ฟีเจอร์นี้ในเครื่องมือสำหรับนักพัฒนาเว็บจะมีประโยชน์อย่างยิ่งเมื่อมีเหตุการณ์แบบไม่พร้อมกันหลายเหตุการณ์เกิดขึ้นโดยสัมพันธ์กัน หรือหากมีการตรวจพบข้อยกเว้นที่ตรวจไม่พบจากภายในโค้ดเรียกกลับแบบไม่พร้อมกัน

ลองใช้งานใน Chrome หากคุณมีความคิดเห็นเกี่ยวกับฟีเจอร์ใหม่นี้ โปรดแจ้งให้เราทราบเกี่ยวกับเครื่องมือติดตามข้อบกพร่องของ Chrome DevTools หรือในกลุ่มเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome