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

François Beaufort
François Beaufort

Você acabou de encontrar esse erro de mídia inesperado no console JavaScript do Chrome DevTools?

ou

Então você está no lugar certo. Não se preocupe. Vou explicar o que está causando isso e como corrigir.

O que está causando isso?

Confira abaixo um código JavaScript que reproduz o erro "Uncaught (in promise)" que você está recebendo:

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 necessariamente começa imediatamente após a execução de video.play().

Além disso, desde o Chrome 50, uma chamada play() em um elemento <video> ou <audio> retorna uma promessa, uma função que retorna um único resultado de forma assíncrona. Se a reprodução for bem-sucedida, a promessa será cumprida e o evento playing será acionado 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 o conteúdo do vídeo de forma assíncrona.
  2. video.pause() interrompe o carregamento do vídeo porque ele ainda não está pronto.
  3. video.play() rejeita de forma assíncrona e com som.

Como não estamos processando a promessa de reprodução de vídeo no código, uma mensagem de erro aparece 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. Confira a promessa retornada pela função play para saber se ela foi rejeitada. Vale observar que a promessa não será cumprida até que a reprodução seja realmente iniciada, o que significa que o código dentro do then() não será executado até que a mídia esteja sendo 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: Abrir 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 este exemplo simples, mas e se você usar video.play() para reproduzir um vídeo mais tarde?

Vou contar um segredo para você. Não é necessário usar video.play(). Você pode usar video.load(). Confira como:

O que fazer

Exemplo: Buscar e reproduzir

<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 à promessa do Google Play

No momento da escrita, HTMLMediaElement.play() retorna uma promessa no Chrome, Edge, Firefox, Opera e Safari.

Área perigosa

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

Para <video src="not-existing-video.mp4"\>, a promessa play() é rejeitada como esperado, porque 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 houver fontes válidas.

Bug do Chromium