Flutter/Dart:异步递归函数中await未等待问题求助
问题分析
你的代码核心问题在于playNextPlayer方法没有等待播放器完成就提前返回,导致后续的await ourSoundRecorder.stop()直接执行:
positionStream.listen()是订阅流的异步操作,不会阻塞方法执行,playNextPlayer在启动第一个播放器后就立刻完成返回。- 递归调用放在流监听的回调里,这个回调的异步逻辑不会影响
playNextPlayer主方法的完成时机,整个播放序列没有形成完整的等待链。
修复方案
我们可以利用AudioPlayer的onPlayerComplete流(播放完成时触发),将其转换为可等待的Future,确保每个播放器播放完成后再启动下一个,让整个播放序列形成完整的异步等待链。
基础版(顺序播放,无重叠)
if (isDrumsSelected) { final List<AudioPlayer> drumBeatsAudioPlayersList = []; for (int i = 0; i < numberOfBars; i++) { drumBeatsAudioPlayersList.add(AudioPlayer()); await drumBeatsAudioPlayersList[i].setAsset('assets/drum_beats/$drumBeatsFolderSelected/${bpmSliderValue.toInt()}BPM.mp3'); } int n = 0; Future<void> playNextPlayer(int index) async { if (index >= numberOfBars) return; // 启动当前播放器 drumBeatsAudioPlayersList[index].play(); // 等待当前播放器播放完成 await drumBeatsAudioPlayersList[index].onPlayerComplete.first; // 递归播放下一个 await playNextPlayer(index + 1); } await playNextPlayer(n); // 等待整个播放序列完成 /// Stop the recording(& music) and... await ourSoundRecorder.stop(); /// ...play the recording. await recordingPlayer.setFilePath(filePath); await recordingPlayer.setLoopMode(LoopMode.all); await recordingPlayer.play(); }
进阶版(支持提前启动下一个播放器,保留重叠逻辑)
如果需要保留原有的“提前110ms启动下一个播放器”的重叠逻辑,可以调整为以下写法:
if (isDrumsSelected) { final List<AudioPlayer> drumBeatsAudioPlayersList = []; for (int i = 0; i < numberOfBars; i++) { drumBeatsAudioPlayersList.add(AudioPlayer()); await drumBeatsAudioPlayersList[i].setAsset('assets/drum_beats/$drumBeatsFolderSelected/${bpmSliderValue.toInt()}BPM.mp3'); } int n = 0; Future<void> playNextPlayer(int index) async { if (index >= numberOfBars) return; final currentPlayer = drumBeatsAudioPlayersList[index]; currentPlayer.play(); // 提前启动下一个播放器的逻辑 final positionSubscription = currentPlayer.positionStream.listen((position) { final duration = currentPlayer.duration; if (duration != null) { final remaining = duration - position; const timeThreshold = Duration(milliseconds: 110); if (remaining <= timeThreshold && index + 1 < numberOfBars) { drumBeatsAudioPlayersList[index + 1].play(); positionSubscription.cancel(); // 避免重复触发 } } }); // 等待当前播放器播放完成,再递归处理下一个 await currentPlayer.onPlayerComplete.first; await playNextPlayer(index + 1); } await playNextPlayer(n); // 等待所有播放器播放完成 await ourSoundRecorder.stop(); await recordingPlayer.setFilePath(filePath); await recordingPlayer.setLoopMode(LoopMode.all); await recordingPlayer.play(); }
修复核心点
- 每个
playNextPlayer方法都会等待当前播放器的onPlayerComplete事件,确保当前播放器播放完成后再进入下一轮递归。 - 整个递归链形成完整的异步等待关系,只有最后一个播放器播放完成,
await playNextPlayer(n)才会结束,进而执行录音停止逻辑。
内容的提问来源于stack exchange,提问作者bbr
相关产品推荐
相关产品推荐

