尽管Dart是一种单线程语言,但它提供了对futures、streams、background work以及所有其他需要以modern、asynchronous和(在Flutter的情况下)reactive 方式编写的内容的支持。本文介绍了Dart支持后台工作的基础:isolates和事件循环。
Isolates
Isolates是所有Dart代码的运行方式。它就像机器上的一个小空间,有自己的私有内存块和运行事件循环的单个线程。
在C++等许多其他语言中,可以有多个线程共享相同的内存并运行所需的任何代码。不过,在Dart中,每个线程都在自己的内存中,线程只处理事件。
许多Dart应用程序在一个单独的 isolate 中运行所有代码,但如果需要,您可以有多个。如果要执行的计算量太大,如果在主isolate中运行,可能会导致删除帧,那么可以使用isolate。spawn()或flutter的compute()函数。这两个函数都创建一个单独的隔离来进行数字处理,让主isolate可以自由地,重建和呈现小部件树。
两个isolate,每个都有自己的内存和执行线程。
新的isolate得到它自己的事件循环和内存,原始isolate——即使它是这个新isolate的父级——也不允许访问。这就是名字isolate的来源:这些小空间彼此隔离。
事实上,isolate可以一起工作的唯一方法是来回传递消息。一个isolate向另一个发送消息,接收isolate使用其事件循环处理该消息。
isolate可以向其他isolate发送消息。
共享内存的缺乏可能听起来有点严格,尤其是如果你来自java或C++语言,但它对DART编码器有一些关键的好处。
例如,isolate中的内存分配和垃圾回收不需要锁定。只有一个线程,所以如果它不忙,你就知道内存没有变化。这对于Flutter应用程序来说效果很好,有时需要快速构建和删除一堆小部件。
Event loops
现在,您已经基本了解了isolate,让我们深入了解真正使异步代码成为可能的因素:事件循环。
想象一下,一个应用程序的生命在时间线上展开。应用程序启动,应用程序停止,其间发生了一系列事件——磁盘的I/O,用户的手指敲击……各种各样的事情。
你的应用程序无法预测这些事件何时发生或以何种顺序发生,它必须用一个永远不会阻塞的线程来处理所有这些事件。因此,应用程序运行一个事件循环。它从其事件队列中获取最早的事件,对其进行处理,返回到下一个事件,对其进行处理,依此类推,直到事件队列为空。
在应用程序运行的整个过程中——你点击屏幕,东西下载,计时器启动——事件循环就是一个接一个地循环,一次处理一个事件。
事件循环一次处理一个事件。
当操作中断时,线程挂起,等待下一个事件。它可以触发垃圾收集器,喝点咖啡,随便什么。
Dart为异步编程提供的所有高级API和语言功能—futures、streams、async和await—都是在这个简单循环的基础上构建的。
例如,假设您有一个启动网络请求的按钮,如下所示:
RaisedButton(
onPressed: () {
final myFuture = http.get('https://my.image.url');
myFuture.then((resp) {
setImage(resp);
});
},
child: Text('Click me!'),
)
当你运行你的应用程序时,Flutter会构建按钮并将其显示在屏幕上。然后你的应用程序等待。
你的应用程序的事件循环有点空闲,等待下一个事件。与按钮无关的事件可能会进入并得到处理,而按钮会坐在那里等待用户点击它。最终它们会这样做,一个tap事件进入队列。
将拾取该事件以进行处理。Flutter看着它,渲染系统说,“这些坐标与升起的按钮匹配”,所以Flutter执行onPressed函数。该代码启动一个网络请求(返回一个future),并使用then()方法为future注册一个完成处理程序。
就这样。循环已完成对该点击事件的处理,并将其丢弃。
现在,onPressed是RaisedButton的一个属性,网络事件在将来使用回调,但这两种技术都在做相同的基本事情。它们都是告诉Flutter的一种方式,“嘿,稍后,您可能会看到特定类型的事件。当您看到时,请执行这段代码。”
因此,onPressed正在等待点击,而未来正在等待网络数据,但从Dart的角度来看,这两个都只是队列中的事件。
这就是异步编码在Dart中的工作原理。Futures、streams、async和await—这些API都是告诉Dart事件循环的方法,“这里有一些代码,请稍后运行。”
RaisedButton(
onPressed: () {
final myFuture = http.get('https://my.image.url');
myFuture.then((resp) {
setImage(resp);
});
},
child: Text('Click me!'),
)
在您习惯了使用异步代码之后,您将开始到处识别这些模式。了解事件循环将有助于您进一步了解更高级别的API。
Summary
我们快速研究了isolates、事件循环和DART中异步编码的基础。如果您想了解更多详细信息,如微任务队列的工作方式,请参阅过时的、已存档但仍然深受欢迎的文章“The Event Loop and Dart”。
要了解有关Dart中异步的更多信息,请参阅本系列的下一篇文章 Dart asynchronous programming: Futures。