DOMException: a solicitação play() foi interrompida

François Beaufort
François Beaufort

Você acabou de se deparar com esse erro de mídia inesperado no Console JavaScript do Chrome DevTools?

ou

Você está no lugar certo. Não tenha medo. Vou explicar o que está causando esse problema e como corrigi-lo.

O que está causando isso

Veja o código JavaScript abaixo que reproduz o erro "Uncaught (in promise)" que está vendo:

O que não fazer
<video id="video" preload="none" src="https://example.com/file.mp4"></video>

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

O código acima resulta nesta mensagem de erro no Chrome DevTools:

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

Como o vídeo não é carregado devido a preload="none", a reprodução não começa imediatamente após video.play() ser executado.

Além disso, desde o Chrome 50, uma chamada play() em um elemento <video> ou <audio> retorna uma Promise, uma função que retorna um único resultado de forma assíncrona. Se a reprodução for bem-sucedida, a promessa será atendida e o evento playing será disparado ao mesmo tempo. Se a reprodução falhar, a promessa será rejeitada com uma mensagem de erro explicando a falha.

Veja o que está acontecendo:

  1. video.play() começa a carregar conteúdo de vídeo de forma assíncrona.
  2. O video.pause() interrompe o carregamento do vídeo porque ele ainda não está pronto.
  3. O video.play() rejeita em voz alta de maneira assíncrona.

Como não estamos processando a promessa de reprodução de vídeo no nosso código, uma mensagem de erro é exibida no Chrome DevTools.

Como corrigir o problema

Agora que entendemos a causa raiz, vamos ver o que podemos fazer para corrigir isso.

Primeiro, nunca presuma que um elemento de mídia (vídeo ou áudio) será reproduzido. Observe a promessa retornada pela função play para ver se ela foi rejeitada. A promessa não será atendida até que a reprodução seja iniciada, o que significa que o código dentro do then() não será executado até que a mídia seja reproduzida.

O que fazer

Exemplo: reprodução automática

<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>
O que fazer

Exemplo: reproduzir e pausar

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

Isso é ótimo para esse exemplo simples, mas e se você usar video.play() para reproduzir um vídeo mais tarde?

Vou contar um segredo. Não é necessário usar video.play(), você pode usar video.load(). Veja como fazer isso:

O que fazer

Exemplo: Buscar e jogar

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

Suporte a promessas do Google Play

No momento em que este artigo foi escrito, HTMLMediaElement.play() retornava uma promessa no Chrome, Edge, Firefox, Opera e Safari.

Zona de perigo

<source> em <video> faz com que a promessa de play() nunca seja rejeitada.

Para <video src="not-existing-video.mp4"\>, a promessa play() é rejeitada conforme esperado, já que o vídeo não existe. Para <video><source src="not-existing-video.mp4" type='video/mp4'></video>, a promessa play() nunca é rejeitada. Isso só acontece se não há fontes válidas.

Bug do Chromium