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

François Beaufort
François Beaufort

Chrome DevTools で予期しないメディアエラーに遭遇しましたか? JavaScript コンソールを使用できますか?

または

君ならきっといるのよ。恐れる必要はありません。この問題の原因修正方法をご説明いたします。

原因

以下は、「Unc catch (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() 呼び出し 要素は、単一の結果を返す関数である Promise を返します。 使用できます。再生が成功すると、Promise が解決され、 playing イベントが同時に発生します。再生が失敗した場合、Promise は エラー メッセージとともに、失敗を示すエラーが表示されます。

今後は次のような影響が出る可能性があります。

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

コードでは動画再生の約束を処理していないため、エラー メッセージが表示されます。 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 Promise のサポート

執筆時点で、HTMLMediaElement.play() は Promise を返します。 Chrome、Edge、Firefox、Opera、Safari

危険ゾーン

<video> 内の <source> により、play() Promise が拒否されなくなる

<video src="not-existing-video.mp4"\> の場合、play() Promise は次のように拒否されます。 動画が存在しないためです。<video><source src="not-existing-video.mp4" type='video/mp4'></video> の場合、play() Promise 拒否されることはありません。これは、有効なソースがない場合にのみ発生します。

Chromium のバグ