您是否在 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 必須等到播放實際開始後才會完成,也就是說,媒體必須播放後,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 中傳回承諾。
危險區
<video> 中的 <source> 會讓 play() 承諾永遠不會遭到拒絕
對於 <video src="not-existing-video.mp4"\>,由於影片不存在,因此 play() 應許條件會如預期遭到拒絕。針對 <video><source
src="not-existing-video.mp4" type='video/mp4'></video>,play() 承諾永遠不會拒絕。只有在沒有有效來源時才會發生。