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

François Beaufort
François Beaufort

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

أو

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

ما سبب ذلك؟

في ما يلي بعض رموز JavaScript التي تعيد إنتاج خطأ "Uncaught (intent)" الذي يظهر لك:

الإجراءات غير المُوصى بها
<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().

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

إليك ما يحدث:

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

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

كيفية حلّ المشكلة

الآن بعد أن فهمنا السبب الجذري، فلنرَ ما يمكننا فعله لإصلاح هذه المشكلة.

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

خطأ Chromium