DOMException - คำขอ play() ถูกขัดจังหวะ

François Beaufort
François Beaufort

คุณเพิ่งพบข้อผิดพลาดเกี่ยวกับสื่อที่ไม่คาดคิดนี้ในเครื่องมือสำหรับนักพัฒนาเว็บใน Chrome คอนโซล JavaScript หรือไม่

หรือ

งั้นคุณก็มาถูกที่แล้ว อย่ากลัว ฉันจะอธิบายสาเหตุของปัญหานี้และวิธีแก้ไข

ปัญหานี้เกิดจากอะไร

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

สิ่งที่จะเกิดขึ้นมีดังนี้

  1. video.play() เริ่มโหลดเนื้อหาวิดีโอแบบไม่พร้อมกัน
  2. video.pause() ขัดจังหวะการโหลดวิดีโอเนื่องจากยังไม่พร้อม
  3. 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() ไม่เคยปฏิเสธ ซึ่งจะเกิดขึ้นเฉพาะในกรณีที่ไม่มีแหล่งที่มาที่ถูกต้องเท่านั้น

ข้อบกพร่องของ Chromium