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 ของเบราว์เซอร์ส่วนใหญ่นั้นเป็นแบบอะซิงโครนัสในบางลักษณะ คุณอาจต้องการใช้ฟังก์ชันแบบนี้ในบางสถานการณ์
