[英]How to run async functions sequentially on a React-Native app, using Expo
結合以下兩種解決方案,我寫道:
const startMusic = async () => {
let currentSong
let songPath
const songArray = [
{ path: require("../assets/sounds/Katsu.mp3"), song: mainTheme },
{ path: require("../assets/sounds/MainTheme2.mp3"), song: mainTheme2 },
{ path: require("../assets/sounds/MainTheme3.mp3"), song: mainTheme3 },
]
for (var i = 0; i < songArray.length; i++) {
currentSong = songArray[i].song
songPath = songArray[i].path
try {
await currentSong.loadAsync(songPath)
await currentSong.playAsync()
// setSoundObject(currentSong)
console.log("Music will start")
return new Promise(resolve => {
currentSong.setOnPlaybackStatusUpdate(playbackStatus => {
if (playbackStatus.didJustFinish) {
console.log("Just finished playing")
resolve()
}
})
})
} catch (error) {
console.log(`Error: ${error}`)
return
}
}
}
這實際上播放了這首歌,並且控制台日志准時出現(“剛完成播放”恰好在歌曲結束時發生)我試圖弄清楚如何播放下一首歌曲..以及它如何知道它何時到達最后一首歌?
return new Promise(resolve => {
currentSong.setOnPlaybackStatusUpdate(playbackStatus => {
if (playbackStatus.didJustFinish) {
console.log("Just finished playing")
resolve()
}
})
}).then(() => console.log("Next song?"))
想知道如何在“剛完成播放”之后將.then
放到控制台日志中,我只是想看看如何實際將下一首歌曲放在那里(當然,告訴它何時 go 回到第一首數組中的歌曲)
使用用於聲音文件的 expo-av 庫為反應原生應用程序分配任務。 現在,該應用程序在負責播放應用程序背景音樂的上下文文件中設置了startMusic
function。 它現在只有一首歌:
const startMusic = async () => {
try {
await mainTheme.loadAsync(require("../assets/sounds/Katsu.mp3"))
await mainTheme.playAsync()
setSoundObject(mainTheme)
console.log("The first song is playing! Enjoy!")
} catch (error) {
console.log(`Couldnt load main theme: ${error}`)
return
}
}
它在主屏幕組件的文件中使用,如下所示:
const { startMusic } = useContext(MusicContext)
useEffect(() => {
startMusic()
}, [])
對於第二首歌,我在 MusicContext 文件中寫了另一個 const:
const secondSong = async () => {
try {
await mainTheme2.loadAsync(require("../assets/sounds/MainTheme2.mp3"))
await mainTheme2.playAsync()
setSoundObject(mainTheme2)
console.log("Now playing the second track. Enjoy!")
} catch (error) {
console.log(`Could not play the second song: ${error}`)
return
}
}
Annnnnd……這就是我的麻煩所在。 我知道這行不通,但我在組件文件中寫了這個,以嘗試在第一首歌曲之后播放第二首歌曲
useEffect(() => {
startMusic()
.then(secondSong())
}, [])
我知道還有更多,但我遇到了麻煩。
您的代碼的問題不僅僅是運行一個 function 一個又一個(這就像startMusic().then(() => secondSong())
一樣簡單,但仍然無法解決問題),而是您的函數實際上不要等到一首歌曲播放完才解決
You expect this line await mainTheme.playAsync()
to pause function execution until the song has finished, but what it in fact does according to docs https://docs.expo.io/versions/latest/sdk/av/ is exactly only開始播放(無需等待播放完成)
話雖如此,您需要確定播放完成的時刻,然后創建一個 Promise 僅在播放完成后才能解析,這樣您的第二首歌曲只能在第一首歌曲之后開始
在沒有錯誤處理等的最簡單的形式中,它看起來像這樣
const startAndWaitForCompletion = async () => {
try {
await mainTheme.loadAsync(require('../assets/sounds/Katsu.mp3'))
await mainTheme.playAsync()
console.log('will start playing soon')
return new Promise((resolve) => {
mainTheme.setOnPlaybackStatusUpdate(playbackStatus => {
if (playbackStatus.didJustFinish) {
console.log('finished playing')
resolve()
}
}
})
} catch (error) {
console.log('error', error)
}
}
訣竅當然是.setOnPlaybackStatusUpdate
監聽器,它會經常被調用並顯示播放狀態,通過分析狀態你可以知道歌曲已經播放完畢。 如果您滾動到我鏈接的頁面底部,您會發現其他帶有狀態更新的示例
更新
const startAndWaitForCompletion = async (playbackObject, file) => {
try {
await playbackObject.loadAsync(file)
await playbackObject.playAsync()
console.log('will start playing soon')
return new Promise((resolve) => {
playbackObject.setOnPlaybackStatusUpdate(playbackStatus => {
if (playbackStatus.didJustFinish) {
console.log('finished playing')
resolve()
}
}
})
} catch (error) {
console.log('error', error)
}
}
////
const songs = [
{ path: require('../assets/sounds/Katsu.mp3'), song: mainTheme },
{ path: require('../assets/sounds/MainTheme2.mp3'), song: mainTheme2 },
{ path: require('../assets/sounds/MainTheme3.mp3'), song: mainTheme3 },
]
useEffect(() => {
(async () => {
for (let i = 0; i < songs.length; i++) {
await startAndWaitForCompletion(songs[i].song, songs[i].path)
}
})()
}, [])
我認為您需要重新考慮這個問題/解決方案以使其更加抽象。
而不是為你想要播放的每一首歌曲制作一個新的 const 和 promise (正如你所說,這不可行,並且不可擴展,比如如果你想要 10 首歌曲而不是 2 首),制作“startMusic”一個 function 播放一組歌曲(每個數組索引都是 MP3 的文件路徑,如您的示例所示),然后根據需要解析/拒絕 promise。
快速的“startMusic”重寫:
const startMusic(songArray) = async () => {
for (var songIndex in songArray) {
try {
await mainTheme.loadAsync(require(songArray[songIndex]))
await mainTheme.playAsync()
setSoundObject(mainTheme)
console.log("Song #", songIndex, "of ", songArray.length " is playing. Enjoy!")
} catch (error) {
console.log(`Couldnt load song: ${error}`)
return
}
}
}
如果上面的 for-try-catch 結構不起作用,“Promise.all”鏈在這里也很有用: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/ Global_Objects/承諾/全部
不熟悉 expo-av 庫,因此可能需要注意一些特定的怪癖,但我認為將“startMusic”重寫為抽象的 function 播放“N”首歌曲的數組是更好的選擇方法,並將最小化/消除您的問題。
.then .then()
接受 function 但您通過調用secondSong
提供了 function 執行的結果。
做:
useEffect(() => {
startMusic()
.then(() => secondSong())
}, [])
或者在secondSong
之后擺脫()
:
useEffect(() => {
startMusic()
.then(secondSong)
}, [])
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.