​ 学习 Dart 的异步编程时,需要对异步编程所涉及的相关知识体系进行梳理,我们可根据以下几个发问来逐个了解异步编程涉及的内容:

  • 为什么需要异步编程?
  • 异步编程的内在机制是什么?
  • Dart 中如何进行异步编程?

isolate:

Dart是单“线程”语言:

Dart 代码在某个 isolate 的上下文中运行,该 isolate 拥有 Dart 代码所需的所有内存。当Dart 代码正在执行时,同一个 isolate 中的其他代码都无法运行,更通俗地讲,Dart 一次执行一个操作,这意味着只要一个操作正在执行,它就不会被任何其他 Dart 代码中断。这就引发出以下问题:

1,如果 Dart 代码正在执行长耗时计算,或者等待 I/O,将会导致程序冻结;

2,如果需要同时运行不同的 Dart 代码,就需要将这些代码放在不同的 isolate 环境中执行 ;

3, 如何在 Dart 中编写异步代码,进行异步操作?(所谓异步操作,指的是当你的程序在等待其它操作完成时,可以让其先完成其它部分的操作)

我们先放码出来:

main() {
fallInLove();//谈恋爱
House newHourse = buyHouse();//买房(耗时、费力操作)
livingHouse(newHourse);//买完房子之后住进新房
marry();//结婚
haveChild();//生娃
}

如上所示,在这种情况下,只有当第一条语句执行完之后,后面的语句才能接着按顺序执行;

大部分人的人生三件事:买房,结婚,生娃

这三件大事,如果用 Dart 语言来实现:

如果是在同步操作中,并且家里没矿,六个钱包也凑不齐首付的话,你的人生就会冻结- -等着买完房之后,才能结婚,结完婚之后才能生娃;

有没有什么操作可以实现先结婚,再生娃,再买房呢?答案是有的:如果将买房(buyHouse())放在异步操作里,这时候你要给你的丈母娘及老婆一个承诺:以后有钱了再买房!现在先结婚、生娃!详情我们后文再叙,这里先提出这么个设想;

event loop:

当开始运行 Flutter 或者 Dart 应用程序时,一个 isolate 就会被创建并启动,当 isolate 创建成功时,Dart 就会进行如下三个事项:

1,初始化两个先进先出(FIFO)队列:一个称为 MicroTask 队列,另外一个称为 Event 队列;

2,执行main()方法,且执行完成后,进入3

3,启动 Event Loop

4,开始处理两个队列中的元素(两个队列的执行有先后顺序,见后文)

也就是说,每个 isolate 中都会有且只有一个Event Loop(事件循环)和两个队列(MicroTask Queue、Event Queue ); Event Loop 将根据MicroTask队列和Event队列里的内容来驱动代码的执行方式和顺序。

那么问题来了:

1,Event Loop是什么?用来干啥的?

2,MicroTask队列和Event队列都分别是什么?有什么用?

3,两者有什么区别?

Event Loop是一个定期唤醒的无限循环:它在MicroTask、Event队列中查找是否有需要运行的任务。如果队列中的任务存在,则当且仅当CPU空闲时,Event Loop将它们放入运行堆栈执行。

MicroTask Queue用于非常短的,需要异步运行的操作,考虑如下场景:想要在稍后完成一些任务但又希望是在执行下一个Event队列之前;一般使用dart:async库中的scheduleMicrotask方法来实现;

Event Queue(事件队列)包含所有外部事件:

  • I/O,
  • 手势,
  • 鼠标事件,
  • 绘图事件,
  • 计时器,
  • isolate 之间的消息
  • Future

每次外部事件被触发时,要执行的相应代码都会被添加到 Event Queue 中,当MicroTask队列中没有任何内容时,Event Loop才会从Event 队列中取出第一项来处理;需要重点关注的是,Future也会被添加到 Event 队列中

当main()方法执行完成后,event loop开始它的工作,

1,先从 microtask 队列以先进先出的方式取出并执行完所有内容;

2,从event 队列中取出并处理第一项;

3,重复上述两个步骤直到两个队列都没有任何内容可执行

综上所述,可以由如下简化图来表示:

Future:

Future 通常指的是异步运行的任务,它会在未来某个时间点完成,这里的完成有两层含义:成功或者失败,成功时返回任务执行的结果(注意:这里的结果指的是 Future< T> 返回范型T的对象),失败时返回错误;

当实例化一个 Future 的时候:

  • 该 Future 的一个实例被创建并记录在由 Dart 管理的内部数组中;
  • 需要由此 Future 执行的代码直接被推送到 Event 队列中;
  • Future 实例返回一个未完成状态的 Future 对象;
  • 如果 Future 语句之后还有其它的话(不是 Future 包含着的代码),则继续执行下一个同步代码;
  • Future 完成后,then()方法及catchError()方法里的代码将会被执行;
import 'dart:async';

void main() {
fallInLove(); //谈恋爱;
handleHouse(); //买房、入住(耗时、费用的操作)
marry(); //结婚
haveChild(); //生娃
} ///进行买房 [buyHouse]、入住[livingHouse]等操作
void handleHouse() {
Future<House> house = buyHouse();
house.then((_) {
livingHouse();
});
} class House {} Future<House> buyHouse() {
Future<House> result = Future(() {
print('buyHouse');
});
return result;
} void livingHouse() {
print('livingHouse');
} void marry() {
print('marry');
} void haveChild() {
print('haveChild');
} void fallInLove() {
print('fall in love');
}

我们来分析上述代码的执行顺序:

  1. main()方法中开始执行同步代码,首先执行fallInLove();
  2. 执行handleHouse()方法,将Future里的(){print('buyHouse');}加入 Event 队列;
  3. 执行marry()方法;
  4. 执行haveChild()方法;
  5. 执行到这里的时候,main()方法已经执行完了,Event Loop开始处理两个队列中的元素,如前面的分析,这时候先查看MicroTask队列有没有需要处理的任务,没有的话,就可以取出Event队列中的第一个任务来执行,在这个例子中,就是开始执行步骤2的(){print('buyHouse');}代码块;
  6. 当上述步骤完成之后,开始执行then()中的方法 livingHouse();;

所以代码的执行结果应该如下所示:

fall in love

marry

haveChild

buyHouse

livingHouse

async/await:

​ 上面的 Future 章节,我们主要使用了 Future 的 API 来达到异步操作的目的,Dart 还为我们提供了一对关键字 async/await 来达成此目的;有了这两个关键字,我们可以像写同步代码那样写异步代码,并且不用使用到 Future的 API (then());

使用 async/await 关键字有以下几个需要注意的点:

  • async 关键字声明的方法,需要返回 Future 对象:有可用值时类型为 Future< T>;无可用值时为 Future< void>;
  • await 关键字只能在 标记为 async 的方法里出现;
  • await 关键字后面跟着的表达式,通常是 Future 对象,如果不是,系统会自动封装成Future 对象;
  • await 表达式会使表达式之后的语句暂停执行(async方法里的执行),直到返回的 Future对象可用;
  • 对应于使用 Future API 中的 catchError()方法,可以将await表达式包在 try/catch进行错误捕获及后续处理;

如下:我们只需要稍微改造handleHouse()方法:

  1. 在方法的 body 前加 async关键字标志
  2. handleHouse()方法的返回类型改为 Future< void>
  3. 在需要耗时的操作方法前加 await关键字标志
///进行买房 [buyHouse]、入住[livingHouse]等操作
Future<void> handleHouse() async {
await buyHouse();
livingHouse();
}

运行代码后的输出效果是与使用Future API 一致的;

总结

本文主要涉及到的概念有:isolate,event loop,future,async/await,理解了这些内容,可以让我们更好地写出、阅读异步编程的相关代码;

参考链接

参考链接1:https://dart.dev/tutorials/language/futures

参考链接2:https://dart.dev/guides/language/language-tour#asynchrony-support

参考链接3:https://dart.dev/articles/archive/event-loop#event-queue-new-future

Dart 异步编程相关概念简述的更多相关文章

  1. Dart异步编程-future

    Dart异步编程包含两部分:Future和Stream 该篇文章中介绍Future 异步编程:Futures Dart是一个单线程编程语言.如果任何代码阻塞线程执行都会导致程序卡死.异步编程防止出现阻 ...

  2. Dart 异步编程(三):详细认识

    基本概念 普通任务按照顺序执行:异步任务将在未来的某个时间执行. 实际演示 void main() { // waitFuture 函数是一个异步函数,阻塞会发生在函数内部 waitFuture(); ...

  3. Dart 异步编程(二):async/await

    对每一个异步任务返回的 Future 对象都使用链式操作-- then ,超过三个以上时,导致"倒三角"现象,降低代码的可阅读性. getHobbies() { post('htt ...

  4. Dart 异步编程(一):初步认识

    由于 Dart 是单线程编程语言,对于进行网络请求和I/O操作,线程将发生阻塞,严重影响依赖于此任务的下一步操作. 通常,在一个阻塞任务之后还有许许多多的任务等待被执行.下一步任务需要上一步任务的结果 ...

  5. dart系列之:dart中的异步编程

    目录 简介 为什么要用异步编程 怎么使用 Future 异步异常处理 在同步函数中调用异步函数 总结 简介 熟悉javascript的朋友应该知道,在ES6中引入了await和async的语法,可以方 ...

  6. 简述异步编程&Promise&异步函数

    前言:文章由本人在学习之余总结巩固思路,不足之前还请指出. 一.异步编程 首先我们先简单来回顾一下同步API和异步API的概念 1.同步API:只有当前的API执行完成之前,才会执行下一个API 例: ...

  7. JavaScript异步编程原理

    众所周知,JavaScript 的执行环境是单线程的,所谓的单线程就是一次只能完成一个任务,其任务的调度方式就是排队,这就和火车站洗手间门口的等待一样,前面的那个人没有搞定,你就只能站在后面排队等着. ...

  8. 深入理解 Python 异步编程(上)

    http://python.jobbole.com/88291/ 前言 很多朋友对异步编程都处于"听说很强大"的认知状态.鲜有在生产项目中使用它.而使用它的同学,则大多数都停留在知 ...

  9. .NET4.5新特性async和await修饰符实现异步编程

    开篇 每一个版本的.net都会引入一些新的特性,这些特性方便开发人员能够快速实现一些功能.虽然.net版本一直在更新,但是新版本对旧版本的程序都是兼容的,在这一点上微软做的还是非常好的.每次学一个新内 ...

随机推荐

  1. DM365 IPNC软件架构具体解释

    于2013在上半场 - 年DM365.DM368的IPNC(网络摄像机)与稳定性测试工作产品结束. 1.简单介绍: TI针对TMS320DM365.DM368进行了DVR和IPNC的应用方案參考.DV ...

  2. Python经常使用内置函数介绍【filter,map,reduce,apply,zip】

    Python是一门非常简洁,非常优雅的语言,其非常多内置函数结合起来使用,能够使用非常少的代码来实现非常多复杂的功能,假设相同的功能要让C/C++/Java来实现的话,可能会头大,事实上Python是 ...

  3. 在vs code中使用dotnet watch run

    只需要在csproj文件中加入一行: <ItemGroup> <PackageReference Include="Microsoft.AspNetCore.App&quo ...

  4. 新版本MenuDemo——使用Duilib模拟Windows本机菜单

    相信玩Duilib朋友已经开始期待一个很长的文章.由于我的文章在一周前公布--"无焦点窗体的实现"里面提到了无焦点窗体在菜单里面的应用,并承诺大家,写一个关于Menu实现的Demo ...

  5. WPF窗体隐藏鼠标光标的方法

    原文:WPF窗体隐藏鼠标光标的方法 要引用 System.Windows.Input;   Mouse.OverrideCursor = Cursors.None; 去掉 Override 则使用: ...

  6. 狄利克雷过程(Dirichlet Process)

    0. 引入 现观察得到两个样本 θ1,θ2,来推测它们可能来自的分布: 假设来自于连续型概率密度函数, θ1,θ2∼H(θ) 则 θ1,θ2 相等的概率为 0,p(θ1=θ2)=0 概率为 0,不代表 ...

  7. WPF 图片灰度处理

    原文:WPF 图片灰度处理 文章的内容是来自微软中文技术论坛的一个帖子,当时是想将一段将图片灰度处理的代码转换为XAML的一个样式,在这里要谢谢 Xiao Yan Qiang.Sheldon _Xia ...

  8. 2-17-MySQL读写分离-mysql-proxy

        实验环境: mysql-proxy服务端:        xuegod1              IP:192.168.10.31 mysql服务器(主,负责写)服务端:xuegod2    ...

  9. .net与.net core学习目录

    .net C#调用python 模拟请求(模拟header/gzip解压/泛型) C#控制台关闭之前做一些操作 C# 元组.匿名对象.ref&out DataTable转换为Entity(反射 ...

  10. 高性能mysql笔记 第一章 mysql架构

    1.1  mysql逻辑结构 第一层: 负责连接处理,授权认证,安全等事情 第二层:负责mysql的大部分核心功能 ,查询解析,分析,优化,缓存和所有的内置函数,所有跨存储引擎的功能都在这一层实现,, ...