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

François Beaufort
François Beaufort

האם נתקלת בשגיאת מדיה בלתי צפויה זו במסוף JavaScript של כלי הפיתוח ל-Chrome?

או

הגעת למקום הנכון. אל דאגה. אסביר לך מה גורם לכך ואיך לפתור את הבעיה.

מה גורם לכך

לפניכם קוד JavaScript שמשחזר את השגיאה "Uncaught (inסור)" שאתם רואים:

מה אסור לעשות
<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() באופן אסינכרוני.

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

איך לתקן אותה

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

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

מה מותר לעשות

דוגמה: 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