Dart中await与unawaited的区别是什么?结合代码示例解析
Dart 中
await 和 unawaited 的区别 两者都是Dart异步生态里的常用能力,核心差异体现在对传入Future的等待策略、异常传播逻辑、代码执行流程的影响上:
await是Dart的内置关键字:调用后会直接挂起当前异步函数的执行,直到后面跟着的Future执行完毕才会继续走后续代码。如果Future执行抛出异常,异常会沿着当前调用栈直接抛出,可以在当前函数内用try/catch捕获。unawaited是dart:async库提供的普通工具函数:调用后不会等待传入的Future执行,会立刻同步执行后续代码。它本身没有任何异步调度逻辑,唯一的核心作用是告诉Dart静态分析器:我故意不等待这个Future,不要抛出"未等待异步返回值"的 lint 警告。需要注意的是,它不会帮你捕获异常,如果传入的Future执行出错,异常会直接进入全局异步异常队列,不会在当前调用栈抛出。
结合音频播放器代码的场景解释
对应的示例代码如下:
AudioPlayer getAudioPlayer() => AudioPlayer(); extension AudioPlayerExtension on AudioPlayer { Future<void> replay() async { await stop(); await seek(null); unawaited(play()); } }
这个replay重播方法的写法非常典型,刚好把两个能力的适用场景区分得很清楚:
- 前两步
await stop()、await seek(null)必须加await
音频重播的流程有严格的先后依赖:必须等当前正在播放的音频完全停止、并且播放指针已经重置到音频起始位置这两个操作全部执行完成,才能启动新的播放。如果这里去掉await直接调用,会出现操作竞态:可能stop还没执行完就开始播新音频,或者seek还没生效播放已经启动,最终出现旧音频没停、播放位置不对等异常。
同时加await之后,如果stop或者seek执行出错(比如音频资源被释放、播放器内部报错),异常会直接在replay的调用栈抛出,调用方可以正常捕获处理。 - 最后一步
play()用unawaited包裹是合理选择
当停止、重置进度两个前置操作都完成后,启动播放的动作不需要等整个音频播放完成再返回——重播方法的职责只是触发"从头播放"这个动作,不需要阻塞等待音频播完。如果这里写成await play(),整个replay方法会一直挂起,直到音频从头到尾播放结束才会把控制权交还给调用方,完全不符合重播功能的交互预期。
这里如果直接裸写play(),Dart的lint规则会报警告,提示你有一个Future没有被等待,存在异常漏处理的风险。套一层unawaited就是明确给分析器传递信号:这里我就是要发后即忘,不需要告警。
要额外注意:这种写法下如果play()启动播放时抛出异常(比如音频文件损坏、音频权限被拒绝),异常不会被replay方法的调用方捕获,会走全局异步异常处理。如果需要捕获这部分异常,要么给play单独挂载catchError,要么在确实需要的场景下改用await。
适用场景总结
- 后续逻辑依赖当前异步操作的执行结果、需要严格保证执行顺序、需要在当前调用栈捕获异常时,用
await。 - 异步操作属于发后即忘的触发类逻辑、后续代码不依赖它的执行结果、已经清楚异常处理路径时,用
unawaited包裹消除lint警告即可。
内容的提问来源于stack exchange,提问作者Hasancan Çakıcıoğlu
相关产品推荐
相关产品推荐

