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 版起,在 <video><audio> 上呼叫 play() 元素會傳回 Promise,也就是傳回單一結果的函式 以非同步方式載入物件如果播放成功,代表 Promise 已執行完畢, playing 事件會同時觸發。如果播放失敗, Promise 表示 並會顯示說明失敗的錯誤訊息

範例如下:

  1. video.play() 會開始以非同步方式載入影片內容。
  2. video.pause() 在影片尚未就緒的情況下中斷載入。
  3. video.play() 會以非同步方式大聲拒絕。

由於我們未在程式碼中處理影片播放 Promise,因此系統會顯示錯誤訊息 顯示在 Chrome 開發人員工具中

修正方式

我們已瞭解根本原因,接著來看看該如何解決這個問題。

首先,請不用假設都會播放媒體元素 (影片或音訊)。查看 play 函式傳回的 Promise,確認是否遭到拒絕。是 值得注意的是,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

危險區域

<video> 中的 <source>play() 承諾絕不會拒絕

針對 <video src="not-existing-video.mp4"\>play() 承諾拒絕為 確認影片不存在針對 <video><source src="not-existing-video.mp4" type='video/mp4'></video>play()承諾 一律不會拒絕只有在沒有有效的來源時才會發生這種情況。

Chromium 錯誤