ค้นหาเว็บไซต์

javascript - console.log() แบบอะซิงค์หรือซิงค์?


ฉันกำลังอ่านหนังสือ Async Javascript โดย Trevor Burnham ตอนนี้ หนังสือเล่มนี้เป็นหนังสือที่ยอดเยี่ยมมากจนถึงตอนนี้

เขาพูดถึงโค้ดตัวอย่างนี้และ console.log ที่เป็น 'async' ในคอนโซลของ Safari และ Chrome น่าเสียดายที่ฉันไม่สามารถทำซ้ำสิ่งนี้ได้ นี่คือโค้ด:

var obj = {}; 
console.log(obj); 
obj.foo = 'bar';
// my outcome: Object{}; 'bar';
// The book outcome: {foo:bar};

ถ้านี่เป็นแบบอะซิงโครนัส ฉันคาดว่าผลลัพธ์จะเป็นผลลัพธ์ของหนังสือ console.log() จะถูกวางในคิวเหตุการณ์จนกว่ารหัสทั้งหมดจะถูกดำเนินการ จากนั้นมันจะถูกรันและมันจะมีคุณสมบัติ bar

ดูเหมือนว่ามันกำลังทำงานแบบซิงโครนัส

ฉันกำลังรันโค้ดนี้ผิดหรือเปล่า? จริงๆ แล้ว console.log เป็นแบบอะซิงค์หรือ?

คำตอบ #1

console.log ไม่ใช่มาตรฐาน ดังนั้นพฤติกรรมของมันจึงไม่ถูกกำหนดแน่นอน และสามารถเปลี่ยนแปลงได้ง่ายจากการออกเวอร์ชันเครื่องมือสำหรับนักพัฒนา หนังสือของคุณอาจล้าสมัยแล้ว เช่นเดียวกับคำตอบของฉันที่จะล้าสมัยในไม่ช้า

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

เรายังไม่รู้จริงๆ ว่าจะเกิดอะไรขึ้นตอนนั้น (ก็โอเค เราอาจรู้ได้ เพราะว่า Firebug, Chrome Devtools และ Opera Dragonfly เป็นโอเพ่นซอร์สทั้งหมด) คอนโซลจะต้องเก็บค่าที่ล็อกไว้บางที่ และจะแสดงค่าพวกนั้นบนหน้าจอ การเรนเดอร์จะเกิดขึ้นแบบอะซิงโครนัสอย่างแน่นอน (ถูกจำกัดอัตราเพื่อจำกัดการอัปเดต) เช่นเดียวกับการโต้ตอบในอนาคตกับวัตถุที่ล็อกไว้ในคอนโซล (เช่น การขยายคุณสมบัติของวัตถุ)

ดังนั้นคอนโซลอาจจะทำการก็อปปี้ (แปลงเป็นลำดับข้อมูล) ของอ็อบเจ็กต์ที่คุณบันทึก หรือมันจะเก็บการอ้างอิงไปยังอ็อบเจ็กต์เหล่านั้น วิธีแรกไม่ค่อยเหมาะกับอ็อบเจ็กต์ที่ซับซ้อน/ใหญ่ นอกจากนี้ อย่างน้อยการแสดงผลเริ่มต้นในคอนโซลน่าจะจะแสดงสถานะ "ปัจจุบัน" ของอ็อบเจ็กต์ เช่น เมื่อมันถูกบันทึก - ในตัวอย่างของคุณคุณจะเห็น Object {}

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

นี่คือภาพหน้าจอที่ถูกโพสต์ในรายงานข้อบกพร่องเพื่ออธิบายการ 'แก้ไข' ของพวกเขา:

ดังนั้น ค่าบางค่าอาจถูกอ้างอิงไปนานหลังจากที่มีการบันทึกแล้ว และการประเมินค่านี้เป็นแบบ lazy ("เมื่อจำเป็น") ตัวอย่างที่มีชื่อเสียงที่สุดของความไม่ตรงกันนี้ถูกกล่าวถึงในคำถาม Is Chrome's JavaScript console lazy about evaluating arrays?

วิธีแก้ปัญหาคือให้แน่ใจว่าคุณบันทึก snapshot ที่ถูกซีเรียลไลส์ของวัตถุของคุณเสมอ เช่น โดยการทำ console.log(JSON.stringify(obj)) แต่อันนี้จะใช้ได้กับวัตถุที่ไม่เป็นวงจรและขนาดค่อนข้างเล็กเท่านั้น ดูเพิ่มเติมได้ที่ How can I change the default behavior of console.log in Safari?.

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

คำตอบ #2

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

window.console.logSync = (...args) => {
  try {
    args = args.map((arg) => JSON.parse(JSON.stringify(arg)));
    console.log(...args);
  } catch (error) {
    console.log('Error trying to console.logSync()', ...args);
  }
};

นี่สร้างเวอร์ชันแบบกึ่งซิงโครนัสของ console.log แต่มีข้อควรระวังเดียวกันตามที่กล่าวไว้ในคำตอบที่ได้รับการยอมรับ

เนื่องจากดูเหมือนว่า ในขณะนี้ console.log ของเบราว์เซอร์ส่วนใหญ่นั้นเป็นแบบอะซิงโครนัสในบางลักษณะ คุณอาจต้องการใช้ฟังก์ชันแบบนี้ในบางสถานการณ์

บทความที่เกี่ยวข้อง