DOMException — запрос play() был прерван

Франсуа Бофор
François Beaufort

Вы только что наткнулись на эту неожиданную медиа-ошибку в консоли JavaScript Chrome DevTools?

или

Тогда вы находитесь в правильном месте. Не бойтесь. Я объясню , что является причиной этого и как это исправить .

Что является причиной этого

Ниже приведен код JavaScript, который воспроизводит ошибку «Uncaught (in Promise)», которую вы видите:

Не
<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 , вызов play() элемента <video> или <audio> возвращает Promise — функцию, которая асинхронно возвращает один результат. Если воспроизведение прошло успешно, обещание выполняется и одновременно запускается событие playing . Если воспроизведение завершается неудачно, обещание отклоняется вместе с сообщением об ошибке, объясняющим сбой.

Теперь вот что происходит:

  1. video.play() начинает асинхронную загрузку видеоконтента.
  2. video.pause() прерывает загрузку видео, поскольку оно еще не готово.
  3. video.play() громко отклоняет асинхронно.

Поскольку в нашем коде мы не обрабатываем обещание воспроизведения видео, в Chrome DevTools появляется сообщение об ошибке.

Как это исправить

Теперь, когда мы понимаем основную причину, давайте посмотрим, что мы можем сделать, чтобы это исправить.

Во-первых, никогда не предполагайте, что какой-либо медиа-элемент (видео или аудио) будет воспроизводиться. Посмотрите на обещание, возвращаемое функцией play , чтобы узнать, было ли оно отклонено. Стоит отметить, что обещание не будет выполнено до тех пор, пока воспроизведение не начнется, а это означает, что код внутри 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>

Играйте, обещайте поддержку

На момент написания HTMLMediaElement.play() возвращает обещание в Chrome , Edge, Firefox, Opera и Safari .

Опасная зона

<source> внутри <video> заставляет обещание play() никогда не отклоняться

Для <video src="not-existing-video.mp4"\> обещание play() отклоняется, как и ожидалось, поскольку видео не существует. Для <video><source src="not-existing-video.mp4" type='video/mp4'></video> обещание play() никогда не отклоняется. Это происходит только в том случае, если нет достоверных источников.

Ошибка хрома