DOMException - تمت مقاطعة طلب play()

François Beaufort
François Beaufort

هل صادفت للتو هذا الخطأ غير المتوقّع في الوسائط في "وحدة تحكّم JavaScript" ضمن "أدوات مطوّري البرامج في Chrome"؟

أو

أنت في المكان المناسب إذن. لا داعي للقلق. سأشرح سبب حدوث ذلك وكيفية حلّ المشكلة.

سبب حدوث ذلك

في ما يلي بعض رموز 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":

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

بما أنّه لا يتم تحميل الفيديو بسبب preload="none"، لا يبدأ تشغيل الفيديو بالضرورة فور تنفيذ video.play().

بالإضافة إلى ذلك، منذ الإصدار 50 من Chrome، يؤدي استدعاء play() على عنصر <video> أو <audio> إلى عرض وعد، وهي دالة تعرض نتيجة واحدة بشكل غير متزامن. في حال نجاح التشغيل، يتم تنفيذ الوعد ويتم تشغيل الحدث playing في الوقت نفسه. في حال تعذّر تشغيل المحتوى، يتم رفض الوعد مع ظهور رسالة خطأ توضّح سبب التعذّر.

إليك ما يحدث الآن:

  1. يبدأ video.play() بتحميل محتوى الفيديو بشكل غير متزامن.
  2. video.pause() يقاطع تحميل الفيديو لأنّه غير جاهز بعد.
  3. يرفض video.play() الطلبات بشكل غير متزامن بصوت عالٍ.

بما أنّنا لا نتعامل مع وعد تشغيل الفيديو في الرمز البرمجي، تظهر رسالة خطأ في أدوات مطوّري البرامج في Chrome.

كيفية حلها

بعد أن عرفنا السبب الأساسي، لنلقِ نظرة على الإجراءات التي يمكننا اتّخاذها لحلّ هذه المشكلة.

أولاً، لا تفترض أبدًا أنّ عنصر الوسائط (الفيديو أو الصوت) سيتم تشغيله. اطّلِع على الوعد الذي تم إرجاعه بواسطة الدالة 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()، وإليك كيفية إجراء ذلك:

الإجراءات الموصى بها

مثال: Fetch & Play

<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

في وقت كتابة هذه المقالة، يعرض الرمز البرمجي 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() الطلبات أبدًا. ولا يحدث ذلك إلّا في حال عدم توفّر مصادر صالحة.

خطأ في Chromium