簡體   English   中英

如何使用 Expo 在 React-Native 應用程序上按順序運行異步函數

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM