Dart-Event Loop
Dart 程序中处处可见异步代码:库会返回 Future 对象,我们所写的代码也可以通过注册 handler 处理点击,文件 I/O ,timer 等事件。
通过本文了解 Dart 的事件循环架构,可以让你写出更好的异步代码,规避意外。本文你可以学到:处理 future 任务的参数,清楚任务执行的顺序。
阅读本文之前,须了解 Future and Error handling。
[分支]: 如果有任务A和任务B需并行执行,但任务C需要A,B的结果才能执行,要怎么写?在Dart上似乎只能把A,B串行?因为没有线程,或者只能用更重的 isolate 来执行? Pool 代码验证:Pool 并不非用于并发。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 Function taskSimulators(String name, List<Duration> timeToSleep ){ if(timeToSleep == null){ timeToSleep = <Duration>[ Duration(milliseconds: Random().nextInt(100000)), Duration(milliseconds: Random().nextInt(100000)), Duration(milliseconds: Random().nextInt(100000)) ]; } return (){ for(var d in timeToSleep){ for(var j=0;j<999999;++j){ insert(name,j); } Future.delayed(d); print("$name after delayted"); } }; } var pre = "AAAA"; insert(name,j){ if(name.codeUnitAt(0) < pre.codeUnitAt(0)){ print("concurrency !!!!! pre=$pre name=$name"); }else if(name != pre){ pre = name; print("old: $pre new: $name"); } } main() async{ final Pool pool = new Pool(2,timeout: Duration(seconds: 50)); <String>["AAAA","BBBB","CCCC","DDDD","EEEE","FFFF","GGGG"].forEach( (name){ var f = taskSimulators(name, null); pool.withResource(f); print("$name executing"); } ); }Worker 不支持2.0 TODO 学习使用 Isolate ,写一个并发库? (run code in parallel)
Dart 是单线程的,所有任务都会放进Event Loop中,当main函数执行完毕后,就开始依次执行Event Loop中的任务。Loop 中有两个队列:microtask queue / event queue 。Dart 会先执行 microtask 中的任务,然后才到 event 中的任务,即前者会有更高优先级。随时可插队。(TODO: 读一下 pool 的实现也许可以加深了解)。由于 dart 是单线程,所以 Future.delayed 的时间是不准的,可能会由于某个任务执行而延后很多(和js中settimeout一样)。
一些错误例子:
1
2
future.then(/* 为某变量赋值 */);
Timer.run((){ /* 使用上面的变量 */ });
以上代码的问题是,如果 future 内有 await 让出cpu,则timer 会先于then执行。正确的做法如下(第二个then 的 v 为第一个 then 返回的值)。
1
future.then((v){ /* 设置变量 */}).then((v){ /* 使用变量 */ });
如果使用变量的操作比较耗时,可考虑用 Future((){ /* 使用变量 */ } );
包住。
Future(FutureOr computation()) 的实现为:在 Timer.run 中执行 computation 。
使用 Future 可以让 event loop 有机会执行其他任务。
是否可理解 best practice 为:不要在future 的 then 里连续执行,而是交给系统调度?但系统只是FIFO,似乎没啥调度呀?
如何插入一个任务
可通过 dart:async
库:
- 通过
Future
类,插入到 event queue 中 - 通过
scheduleMicrotask()
函数插入到 microtask queue.
scheduleMicrotask
曾用名 runAsync
,现已废弃
使用Future 和 Timer 插入任务的区别是,前者对后者进行了封装,增加了任务完成检测及错误处理等,因此,建议优先考虑 Future 而不是裸调 Timer。
Future.delayed 的涵意是,延时多少秒后,把任务插入队列(而非执行任务)
Tips: web app中绘制动画时,不应使用 Future(Timer/Stream),应该使用 animationFrame (指向 requestAnimatorFrame)
几个要点:
-
Future 的 then 参数里传递的函数会被直接执行而非插入队列
-
如果 Future 对象已经被计算完毕,尔后 then 才被调用,那系统会在 microtask queue 中插入一个任务,以执行 then 参数所指向的函数。
-
Future 和 Future.delay 仅做入列操作而非直接执行任务。
-
Future.value 会在microtask queue 中插入任务回调。
-
Future.sync 构造函数会直接执行参数指向函数,并入列 microtask 。