Dart中Future入参带与不带async时microtask执行顺序不同的原因是什么
Dart中Future.microtask随async修饰出现执行顺序差异的原因
前置基础:Dart事件循环调度规则
Dart的执行流严格遵循以下优先级:
- 首先执行当前调用栈的所有同步代码
- 同步代码执行完毕后,按先进先出顺序清空微任务队列里的所有任务
- 微任务队列清空后,才会取出事件队列的第一个任务执行,重复上述流程
另外两个关键知识点:
Future(computation)构造函数会将传入的computation回调调度到事件队列等待执行- async函数的特性:
- async函数执行到同步代码末尾时,会返回一个未完成的Future
- 该Future不会同步完成,而是会将自身的完成动作调度到微任务队列等待执行
- 如果
Future(computation)的computation返回了一个Future,那么构造出的Future会自动链式等待这个返回的Future完成,才会自身完成 - await只有在等待的Future未完成时才会暂停当前函数让出事件循环,如果等待的Future已经是完成状态,会直接同步执行后续代码
例子1:Future构造入参不带async
对应输出:1 future, 1, 1 microtask, 1 inner future, 2 future
执行流程:
- main函数执行到
await Future(() { ... })时,Future构造函数将回调调度到事件队列,返回未完成的Future F1,main暂停让出事件循环 - 事件循环取出回调执行:
- 同步打印
1 future - 调用
Future.microtask将打印1 microtask的任务M1加入微任务队列 - 调用
Future(() => print('1 inner future'))将该任务E1加入事件队列 - 回调是普通函数,执行完毕返回普通值,F1同步完成
- 因为F1已经完成,await不再暂停,main函数直接同步恢复执行,打印
1,再将打印2 future的任务E2加入事件队列
- 同步打印
- 当前调用栈的所有同步代码执行完毕,开始清空微任务队列,执行M1打印
1 microtask - 微任务队列清空,依次取出事件队列的E1、E2执行,分别打印
1 inner future、2 future
例子2:Future构造入参带async
对应输出:1 future, 1 microtask, 1, 1 inner future, 2 future
执行流程:
- main函数执行到
await Future(() async { ... })时,Future构造函数将回调调度到事件队列,返回未完成的Future F1,main暂停让出事件循环 - 事件循环取出回调执行:
- 同步打印
1 future - 调用
Future.microtask将打印1 microtask的任务M1加入微任务队列 - 调用
Future(() => print('1 inner future'))将该任务E1加入事件队列 - 回调是async函数,同步代码执行完毕,返回未完成的Future F2,
Future(computation)构造函数会让F1链式等待F2完成 - async函数返回的F2不会同步完成,系统会将完成F2的任务M2加入微任务队列
- 同步打印
- 当前调用栈的同步代码执行完毕,开始清空微任务队列:
- 先执行M1,打印
1 microtask - 再执行M2,完成F2,F1也同步完成
- F1完成后,await恢复main函数执行,打印
1,将打印2 future的任务E2加入事件队列
- 先执行M1,打印
- 微任务队列清空,依次取出事件队列的E1、E2执行,分别打印
1 inner future、2 future
核心差异总结
两种场景的唯一区别就是async回调返回了Future,导致外层被await的Future不会同步完成:
- 无async时:回调执行完后外层Future同步完成,main直接恢复执行,所以
print('1')先于微任务队列的M1执行 - 有async时:回调执行完后外层Future需要等待async返回的Future完成,而这个完成动作被调度成了微任务,排在M1之后,所以M1先执行,之后才会恢复main执行
print('1')
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

