DOMException - play() リクエストが中断される

François Beaufort
François Beaufort

Chrome DevTools JavaScript コンソールで、この予期しないメディアエラーに遭遇しましたか?

または

お問い合わせいただきありがとうございます。ご安心ください。この問題の原因解決方法について説明します。

原因

表示される「Uncaught (in promise)」エラーを再現する JavaScript コードを次に示します。

すべきでないこと
<video id="video" preload="none" src="https://example.com/file.mp4"></video>

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

上記のコードを使用すると、Chrome DevTools に次のエラー メッセージが表示されます。

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

preload="none" が原因で動画が読み込まれないため、video.play() の実行直後に動画の再生が開始されるとは限りません。

さらに、Chrome 50 以降、<video> 要素または <audio> 要素に対する play() 呼び出しは、1 つの結果を非同期で返す関数である Promise を返します。再生が成功すると、Promise が満たされ、playing イベントが同時に発生します。再生に失敗した場合、失敗を説明するエラー メッセージとともに Promise が拒否されます。

状況は次のとおりです。

  1. video.play() は、動画コンテンツの非同期読み込みを開始します。
  2. video.pause() は、動画の準備ができていないために動画の読み込みを中断します。
  3. video.play() が非同期で大声で拒否します。

コードで動画再生の Promise を処理していないため、Chrome DevTools にエラー メッセージが表示されます。

解決方法

根本原因がわかったので、この問題を解決するためにできることを見てみましょう。

まず、メディア要素(動画または音声)が再生されると想定しないでください。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 で Promise を返します。

危険地帯

<video> 内の <source> により、play() プロミスが拒否されなくなります。

<video src="not-existing-video.mp4"\> の場合、動画が存在しないため、play() プロミスは想定どおり拒否されます。<video><source src="not-existing-video.mp4" type='video/mp4'></video> の場合、play() Promise は決して拒否されません。有効なソースがない場合にのみ発生します。

Chromium のバグ