您是否在 Chrome 開發人員工具 JavaScript 控制台中意外發現這個非預期的媒體錯誤?
或
你來對地方了。別擔心,我會說明造成這個問題的原因,以及如何修正。
造成這個問題的原因
以下是一些 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 起,對 <video>
或 <audio>
元素的 play()
呼叫會傳回 Promise,這是會以非同步方式傳回單一結果的函式。如果播放成功,承諾就會達成,同時觸發 playing
事件。如果播放失敗,系統會拒絕 Promise,並附上說明失敗原因的錯誤訊息。
以下是發生的情況:
video.play()
會開始以非同步方式載入影片內容。video.pause()
會中斷影片載入作業,因為影片尚未準備就緒。video.play()
以非同步方式大聲拒絕。
由於我們未在程式碼中處理影片播放 Promise,因此 Chrome DevTools 會顯示錯誤訊息。
修正方式
瞭解根本原因後,我們來看看如何解決這個問題。
首先,請勿假設媒體元素 (影片或音訊) 會播放。查看 play
函式傳回的 Promise,瞭解 Promise 是否遭到拒絕。值得注意的是,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 中傳回承諾。
危險區
<video>
中的 <source>
會讓 play()
承諾永遠不會遭到拒絕
對於 <video src="not-existing-video.mp4"\>
,play()
應許會因影片不存在而遭到拒絕,針對 <video><source
src="not-existing-video.mp4" type='video/mp4'></video>
,play()
承諾永遠不會拒絕。只有在沒有有效來源時才會發生。