DOMException : la requête play() a été interrompue.

François Beaufort
François Beaufort

Vous venez de découvrir une erreur multimédia inattendue dans la console JavaScript des outils pour les développeurs Chrome ?

ou

Vous êtes au bon endroit. N'ayez crainte. Je vais vous expliquer la cause du problème et comment le résoudre.

Quelle est la cause du problème ?

Vous trouverez ci-dessous du code JavaScript qui reproduit l'erreur "Non capturé (promis)" qui s'affiche:

À éviter
<video id="video" preload="none" src="https://example.com/file.mp4"></video>

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

Le code ci-dessus génère le message d'erreur suivant dans les outils pour les développeurs Chrome:

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

Comme la vidéo n'est pas chargée en raison d'une preload="none", la lecture ne démarre pas nécessairement immédiatement après l'exécution de video.play().

De plus, depuis Chrome 50, un appel play() sur un élément <video> ou <audio> renvoie une promesse, une fonction qui renvoie un seul résultat de manière asynchrone. Si la lecture aboutit, la promesse est satisfaite et l'événement playing est déclenché en même temps. Si la lecture échoue, la promesse est refusée, avec un message d'erreur expliquant l'échec.

Voici ce qui se passe:

  1. video.play() commence à charger le contenu vidéo de manière asynchrone.
  2. video.pause() interrompt le chargement de la vidéo, car celle-ci n'est pas encore prête.
  3. video.play() rejette fortement les requêtes asynchrones.

Comme nous ne gérons pas la promesse de lecture de la vidéo dans notre code, un message d'erreur s'affiche dans les outils pour les développeurs Chrome.

Solution

Maintenant que nous avons déterminé l'origine du problème, voyons ce que nous pouvons faire pour y remédier.

Tout d'abord, ne partez jamais du principe qu'un élément multimédia (vidéo ou audio) va être lu. Examinez la promesse renvoyée par la fonction play pour voir si elle a été rejetée. Notez que la promesse ne s'exécute qu'une fois la lecture commencée, ce qui signifie que le code à l'intérieur de then() ne s'exécute pas tant que le contenu multimédia n'est pas en cours de lecture.

À faire

Exemple: Lecture automatique

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

Exemple: Lecture et pause

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

C'est très bien pour cet exemple simple, mais que se passe-t-il si vous utilisez video.play() pour pouvoir lire une vidéo ultérieurement ?

Je vais vous révéler un secret. Vous n'avez pas besoin d'utiliser video.play(). Vous pouvez utiliser video.load(). Voici comment procéder:

À faire

Exemple: Explorer et jouer

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

Assistance pour les promesses Play

Au moment de la rédaction de ce document, HTMLMediaElement.play() renvoie une promesse dans Chrome, Edge, Firefox, Opera et Safari.

Zone dangereuse

<source> dans <video> permet de ne jamais rejeter la promesse de play()

Pour <video src="not-existing-video.mp4"\>, la promesse play() est rejetée comme prévu, car la vidéo n'existe pas. Pour <video><source src="not-existing-video.mp4" type='video/mp4'></video>, la promesse play() n'est jamais rejetée. Cela ne se produit que s'il n'y a pas de sources valides.

Bug Chromium