Properly handle the Promise returned by <audio>.play()
See original GitHub issueBackground
In modern browsers, the HTMLMediaElement.play()
method is not always synchronous. According to MDN:
The
HTMLMediaElement.play()
method attempts to begin playback of the media and returns aPromise
which is fulfilled when the playback has been successfully started, and which is rejected if playback fails to begin for any reason (such as permission issues or other problems).
It seems that “permission issues” cover mobile/recent desktop browser decisions to ignore autoplay
. Google has published an article about this here which points to this example page. Running that example on an iPhone indeed reports a permissions issue in the output.
In fact, the “permissions” issue for autoplay
is no longer limited to mobile contexts. Safari now disables autoplay functionality on the Desktop by default as well. Opening Google’s example page in Safari shows this error:
Attempting to play automatically… The play() Promise rejected! Use the Play button instead. {“code”:35,“name”:“NotAllowedError”,“message”:“The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.”,“line”:124,“column”:47,“sourceURL”:“https://googlechrome.github.io/samples/play-return-promise/”}
Google has also published a document that goes into greater detail about the causes of this. Of particular interest is this note:
Note: Calling
video.pause()
isn’t the only way to interrupt a video. You can entirely reset the video playback state, including the buffer, withvideo.load()
andvideo.src = ''
.
(Note that the same applies for audio
.)
In APlayer
Currently, APlayer does not attempt to capture the returned Promise. This is the latest from time of writing: https://github.com/MoePlayer/APlayer/blob/81002c0bf93425884bd565147ad3c2b85762ca44/src/APlayer.js#L738-L745
Proper handling of the <audio>
element requires handling the Promise. It is exceptionally simple to cause exceptions with the <audio>
API (see: #155).
In fact, the basic Demo Page shows this in the console directly after the APlayer version report:
[Error] Unhandled Promise Rejection: NotAllowedError (DOM Exception 35): The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
I have created a JSFiddle that shows a range of ways to deal with the HTMLMediaElement API. Try each of the four buttons and check the JavaScript console and output block. Please also take a look at the implementation of the best()
function. It shows a unified way to handle the API in environments that return Promises and undefined
.
I would highly recommend that the APlayer codebase be updated to properly handle Promises. This may also require holding a reference to the most recent Promise returned by the HTMLMediaElement.play()
function (as well as a “playPending” boolean?) so that you can chain other calls (pause()
, load()
, etc.) off of it.
Issue Analytics
- State:
- Created 6 years ago
- Reactions:2
- Comments:32 (28 by maintainers)
@ericdrobinson You are right, I fix playedPromise overwritten in your first way(https://github.com/MoePlayer/APlayer/commit/cf5972879a27459445aaf730f7777c9400667881#r27989819) in fb4211a0b050a065f1ecc3e5eb37b97f0bd96ffc.
Does this make sense?
@ericdrobinson Glad to see it’s finally resolved. Gonna keep up with it.