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