คุณเพิ่งพบข้อผิดพลาดเกี่ยวกับสื่อที่ไม่คาดคิดนี้ในคอนโซล JavaScript ของเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome ใช่ไหม
หรือ
คุณมาถูกที่แล้ว ไม่ต้องกังวล เราจะอธิบายสาเหตุของปัญหานี้และวิธีแก้ไข
สาเหตุที่ทำให้เกิดปัญหานี้
ด้านล่างนี้คือโค้ด JavaScript บางส่วนที่ทำให้เกิดข้อผิดพลาด "Uncaught (in promise)" ที่คุณเห็น
<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
แสดงผลเพื่อดูว่าถูกปฏิเสธหรือไม่ โปรดทราบว่า Promise จะไม่ดำเนินการจนกว่าการเล่นจะเริ่มขึ้นจริง ซึ่งหมายความว่าโค้ดภายใน 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
ขณะเขียนบทความนี้ 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()
จะปฏิเสธไม่ทำงาน กรณีนี้จะเกิดขึ้นก็ต่อเมื่อไม่มีแหล่งที่มาที่ถูกต้อง