先上个图
首先,它以FIFO(先进先出)顺序执行所有微任务。然后,它使事件队列中的第一项出队并处理,然后它重复这个循环:执行所有微任务,然后处理事件队列上的下一事件。
重点:
1、默认情况下微任务队列只有dart的核心代码的事件
2、当事件循环正在执行微任务队列中的任务时,事件队列会卡住:应用程序无法绘制图形、处理鼠标点击、对I/O做出反应等 (所以这种实现的异步也有可能导致页面卡顿)
3、这种实现下延时是有误差的,当您创建一个延迟的任务时,事件将在您指定的时间进入队列。他还是要等待事件队列中它之前的所有事件(包括微任务队列中的每一个事件)全部执行完后,才能得到执行,不插队
Future 和 scheduleMicrotask()
1、Future类,它将一个项目添加到事件队列的末尾。
2、顶级的scheduleMicrotask()函数,它将一个项目添加到微任务队列的末尾(如果一个任务需要在处理任何来自事件队列的事件之前完成,那么你通常应该先执行该函数。如果不能先执行,那么使用 scheduleMicrotask()将这个任务添加到微任务队列中)
await / async
上图
1、仔细看划线部分的执行顺序
main...start A...start B...start C...start C...end A...end main...end --------------------- AA 微任务...1 C执行完毕 B...end B的执行结果 then 微任务...2 -------------------- BB 事件队列加入一项 事件队列加入又一项
1、Future()把执行体加入到了实践队列
2、then()和await后面的代码被添加到了微任务队列,并且在下次循环之后同步顺序执行。
3、如果此时then()里面的函数体是一个死循环,程序就会被卡死,所以 async函数并不是真正意义的异步,他只是一个程序流程控制的工具,控制某些代码在何时执行,并不能实现并发
5、既然能阻塞 那 文件读写、网络请求为啥不阻塞呢?这是因为类似于网络请求、文件读写的IO,我们都可以基于非阻塞调用;
阻塞式调用和非阻塞式调用
阻塞式调用: 调用结果返回之前,当前线程会被挂起,调用线程只有在得到调用结果之后才会继续执行。
非阻塞式调用: 调用执行之后,当前线程不会停止执行,只需要过一段时间来检查一下有没有结果返回即可。
比如网络请求本身使用了Socket通信,而Socket本身提供了select模型,可以进行非阻塞方式的工作;
比如文件读写的IO操作,我们可以使用操作系统提供的基于事件的回调机制;
因为Dart是单线程语言,当遇到延迟的运算(I/O操作),线程中顺序执行的运算就会阻塞,那就app上,用户操作就会感到卡顿,于是通常用异步处理来解决这个问题,当遇到需要延迟的运算时,就会放入延迟运算的队列中,先把不需要延迟的运算先执行,最后再来处理延迟运算。Dart类库有非常多的返回Future或者Stream对象的函数,这些函数被称为异步函数;它们会在设置好一些需要消耗一定时间的操作之后返回,比如I/O操作,而不是等到这个操作完成。
开发中应该保证永远不要阻塞 事件循环。 换句话说,每个请求的 Dart 回调都应该快速完成。 这些对于 await/async,Future.then 也同样适用
Dart是如何完成耗时操作的
Dart完成耗时操作的原理是: 单线程 + 事件循环 + 非阻塞式调用
dio.post返回了一个Future对象,这个对象的函数体会被插入到事件队列,等待下次执行,并不一定是结果,有可能是查询结果的操作
真正的异步 还是需要多线程来完成
isolate
flutter 提供了简单的isolate 使用方式 compute