คุณเพิ่งเจอข้อผิดพลาดสื่อที่ไม่คาดคิดในคอนโซล JavaScript ของ Chrome DevTools ใช่ไหม
หรือ
คุณมาถูกที่แล้ว อย่ากลัว เราจะอธิบายสาเหตุและวิธีแก้ไข
สาเหตุของปัญหานี้คืออะไร
นี่คือโค้ด JavaScript บางส่วนด้านล่างที่จำลองข้อผิดพลาด "ไม่พบ (มีสัญญา)" ที่คุณเห็น
<video id="video" preload="none" src="https://example.com/file.mp4"></video> <script> video.play(); // <-- This is asynchronous! video.pause(); </script>
โค้ดด้านบนจะส่งผลให้เกิดข้อความแสดงข้อผิดพลาดนี้ในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome
_Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause().
เนื่องจาก preload="none"
ไม่ได้โหลดวิดีโอ วิดีโอจึงไม่ได้เริ่มเล่นทันทีหลังจากที่เรียกใช้ video.play()
นอกจากนี้ ตั้งแต่ Chrome 50 การเรียก play()
ในองค์ประกอบ <video>
หรือ <audio>
จะแสดงผล Promise ซึ่งเป็นฟังก์ชันที่แสดงผลลัพธ์เดี่ยวแบบไม่พร้อมกัน หากเล่นสำเร็จ ฟีเจอร์ Promise จะบรรลุผลและเหตุการณ์ playing
จะเริ่มทำงานพร้อมกัน หากเล่นไม่สำเร็จ ระบบจะปฏิเสธ Promise พร้อมกับข้อความแสดงข้อผิดพลาดที่อธิบายความล้มเหลวดังกล่าว
นี่คือสิ่งที่เกิดขึ้น
video.play()
เริ่มโหลดเนื้อหาวิดีโอแบบไม่พร้อมกันvideo.pause()
ขัดจังหวะการโหลดวิดีโอเนื่องจากยังไม่พร้อมใช้งานvideo.play()
ปฏิเสธเสียงดังไม่พร้อมกัน
เนื่องจากเราไม่ได้จัดการการเล่นวิดีโอ Promise ในโค้ดของเรา ข้อความแสดงข้อผิดพลาดจึง ปรากฏในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome
วิธีแก้ไข
เมื่อเข้าใจสาเหตุแล้ว มาดูกันว่าเราจะแก้ปัญหานี้ได้อย่างไรบ้าง
ข้อแรก อย่าคิดเอาเองว่าองค์ประกอบสื่อ (วิดีโอหรือเสียง) จะเล่น ดู Promise ที่ฟังก์ชัน play
แสดงผลเพื่อดูว่าถูกปฏิเสธหรือไม่ เป็นที่น่าสังเกตว่าข้อเสนอจะไม่ประสบความสำเร็จจนกว่าการเล่นจะเริ่มเล่นจริงๆ ซึ่งหมายความว่าโค้ดใน then()
จะไม่ทำงานจนกว่าสื่อจะเล่น
ตัวอย่าง: เล่นอัตโนมัติ
<video id="video" preload="none" src="https://example.com/file.mp4"></video> <script> // Show loading animation. var playPromise = video.play(); if (playPromise !== undefined) { playPromise.then(_ => { // Automatic playback started! // Show playing UI. }) .catch(error => { // Auto-play was prevented // Show paused UI. }); } </script>
ตัวอย่าง: เล่นและหยุดชั่วคราว
<video id="video" preload="none" src="https://example.com/file.mp4"></video> <script> // Show loading animation. var playPromise = video.play(); if (playPromise !== undefined) { playPromise.then(_ => { // Automatic playback started! // Show playing UI. // We can now safely pause video... video.pause(); }) .catch(error => { // Auto-play was prevented // Show paused UI. }); } </script>
ตัวอย่างง่ายๆ นี้เหมาะมาก แต่จะใช้ video.play()
เพื่อให้เล่นวิดีโอในภายหลังได้ล่ะ
เดี๋ยวจะบอกความลับให้นะ คุณไม่จำเป็นต้องใช้ video.play()
โดยสามารถใช้ video.load()
ได้ดังนี้
ตัวอย่าง: ดึงข้อมูลและเล่น
<video id="video"></video> <button id="button"></button> <script> button.addEventListener('click', onButtonClick); function onButtonClick() { // This will allow us to play video later... video.load(); fetchVideoAndPlay(); } function fetchVideoAndPlay() { fetch('https://example.com/file.mp4') .then(response => response.blob()) .then(blob => { video.srcObject = blob; return video.play(); }) .then(_ => { // Video playback started ;) }) .catch(e => { // Video playback failed ;( }) } </script>
การสนับสนุน Play Pro
ในขณะที่เขียน HTMLMediaElement.play()
จะส่งกลับคำสัญญาใน Chrome, Edge, Firefox, Opera และ Safari
โซนอันตราย
<source>
ใน <video>
ทำให้ play()
สัญญาได้เลยว่าจะไม่ถูกปฏิเสธ
สำหรับ <video src="not-existing-video.mp4"\>
คำมั่นสัญญา play()
จะปฏิเสธตามที่คาดไว้เนื่องจากไม่มีวิดีโออยู่ สำหรับ <video><source
src="not-existing-video.mp4" type='video/mp4'></video>
สัญญาของ play()
จะไม่ปฏิเสธเด็ดขาด ซึ่งจะเกิดขึ้นเฉพาะในกรณีที่ไม่มีแหล่งที่มาที่ถูกต้องเท่านั้น