DOMException - Yêu cầu play() bị gián đoạn

François Beaufort
François Beaufort

Bạn vừa bắt gặp lỗi phương tiện không mong muốn này trong Công cụ của Chrome cho nhà phát triển Bảng điều khiển JavaScript không?

hoặc

Vậy là bạn đã đến đúng chỗ rồi. Không phải sợ hãi. Tôi sẽ giải thích nguyên nhân gây ra vấn đề nàycách khắc phục.

Nguyên nhân gây ra vấn đề này

Dưới đây là một số mã JavaScript bên dưới tái tạo lỗi "Chưa nắm bắt (trong lời hứa)" mà bạn đang gặp phải:

Không nên
<video id="video" preload="none" src="https://example.com/file.mp4"></video>

<script>
  video.play(); // <-- This is asynchronous!
  video.pause();
</script>

Mã ở trên dẫn đến thông báo lỗi này trong Công cụ của Chrome cho nhà phát triển:

_Uncaught (in promise) DOMException: The play() request was interrupted by a call to pause().

Vì video không được tải do preload="none", nên tính năng phát lại video sẽ không nhất thiết phải bắt đầu ngay sau khi video.play() được thực thi.

Hơn nữa kể từ Chrome 50, một lệnh gọi play() trên <video> hoặc <audio> phần tử trả về Promise, hàm trả về một kết quả duy nhất một cách không đồng bộ. Nếu phát thành công, Lời hứa được thực hiện và playing sự kiện được kích hoạt cùng một lúc. Nếu không phát được, Promise sẽ bị từ chối cùng với thông báo lỗi giải thích lỗi.

Bây giờ là những gì đang diễn ra:

  1. video.play() bắt đầu tải nội dung video không đồng bộ.
  2. video.pause() làm gián đoạn quá trình tải video do video chưa sẵn sàng.
  3. video.play() từ chối lớn không đồng bộ.

Vì chúng ta không xử lý video Promise (Lời hứa trong đoạn mã) nên sẽ xuất hiện một thông báo lỗi sẽ xuất hiện trong Công cụ của Chrome cho nhà phát triển.

Cách khắc phục

Giờ đây, khi đã hiểu được nguyên nhân gốc rễ, hãy cùng xem chúng ta có thể làm gì để khắc phục tình trạng này.

Trước tiên, đừng bao giờ giả định rằng một thành phần đa phương tiện (video hoặc âm thanh) sẽ phát. Xem Promise được hàm play trả về để xem liệu hàm này có bị từ chối hay không. Đó là đáng chú ý là Lời hứa sẽ không thực hiện được cho đến khi quá trình phát lại thực sự bắt đầu, nghĩa là mã bên trong then() sẽ không thực thi cho đến khi phương tiện đang phát.

Nên

Ví dụ: Tự động phát

<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>
Nên

Ví dụ: Chơi & Tạm dừng

<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>

Thật tuyệt vời cho ví dụ đơn giản này, nhưng nếu bạn sử dụng video.play() làm có thể phát video sau không?

Tôi sẽ cho bạn biết một bí mật. Bạn không phải sử dụng video.play(), bạn có thể sử dụng video.load() và cách thực hiện như sau:

Nên

Ví dụ: Tìm nạp và Chơi

<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>

Hỗ trợ Play Promise

Tại thời điểm viết bài, HTMLMediaElement.play() trả về lời hứa sau Chrome, Edge, Firefox, Opera và Safari.

Vùng nguy hiểm

<source> trong <video> giúp play() lời hứa không bao giờ từ chối

Đối với <video src="not-existing-video.mp4"\>, lời hứa play() sẽ từ chối dưới dạng đúng như dự kiến vì video không tồn tại. Đối với <video><source src="not-existing-video.mp4" type='video/mp4'></video>, lời hứa play() không bao giờ từ chối. Trường hợp này chỉ xảy ra nếu không có nguồn hợp lệ.

Lỗi Chromium