DOMexcept - בקשת play() הופסקה

François Beaufort
François Beaufort

נתקלתם בשגיאת המדיה הלא צפויה הזו במסוף JavaScript של DevTools ב-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().

בנוסף, החל מ-Chrome 50, קריאה ל-play() ברכיב <video> או <audio> מחזירה Promise, פונקציה שמחזירה תוצאה אחת באופן אסינכרוני. אם ההפעלה מצליחה, ה-Promise מתקיים והאירוע playing מופעל באותו זמן. אם ההפעלה נכשלת, ה-Promise נדחה עם הודעת שגיאה שמסבירה את הכישלון.

זה מה שקורה:

  1. video.play() מתחיל לטעון תוכן וידאו באופן אסינכרוני.
  2. video.pause() משבשת את טעינת הסרטון כי הוא עדיין לא מוכן.
  3. video.play() דוחה אסינכרונית בקול רם.

מכיוון שאנחנו לא מטפלים ב-Promise של הפעלת הסרטון בקוד שלנו, מוצגת הודעת שגיאה ב-Chrome DevTools.

איך לתקן אותה

עכשיו, אחרי שהבנו את שורש הבעיה, נראה מה אפשר לעשות כדי לפתור אותה.

קודם כול, אף פעם אל תניחו שרכיב מדיה (וידאו או אודיו) יופעל. בודקים את ה-Promise שהוחזר על ידי הפונקציה play כדי לראות אם הוא נדחה. חשוב לציין שה-Promise לא ימולא עד שההפעלה תתחיל בפועל, כלומר הקוד ב-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>

תמיכה ב'הבטחת 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