本文首发于个人博客
前言
关于异步,相信很多开发者都是经常用到的。不过,不同的语言有不同的处理方式
一般耗时操作的处理
针对如何处理耗时的操作,不同的语言有不同的处理方式。
处理方式一: 多线程,比如
Java
、C++
,Objective C
我们普遍的做法是开启一个新的线程(Thread
),在新的线程中完成这些异步的操作,再通过线程间通信的方式,将拿到的数据传递给主线程。处理方式二: 单线程+事件循环,比如
JavaScript
、Dart
都是基于单线程加事件循环来完成耗时操作的处理。
阻塞式调用和非阻塞式调用
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态。
阻塞式调用: 调用结果返回之前,当前线程会被挂起,调用线程只有在得到调用结果之后才会继续执行。
非阻塞式调用: 调用执行之后,当前线程不会停止执行,只需要过一段时间来检查一下有没有结果返回即可。
例如你平时做饭的时候,把米放在电饭煲里面煮,这时候你可以在旁边坐着等着米饭做好,然后去做菜,这就是阻塞式调用。当然了。你也可以煮饭时候,准备菜,啤酒鸭,麻辣小龙虾。。这就是非阻塞式调用。
Dart事件循环
什么是事件循环
单线程模型中主要就是在维护着一个事件循环(Event Loop)。
事件循环是什么呢?
事实上事件循环并不复杂,它就是将需要处理的一系列事件(包括点击事件、IO事件、网络事件)放在一个事件队列(Event Queue)中。
不断的从事件队列(Event Queue)中取出事件,并执行其对应需要执行的代码块,直到事件队列清空位置。
当我们有一些事件时,比如点击事件、IO事件、网络事件时,它们就会被加入到eventLoop中,当发现事件队列不为空时发现,就会取出事件,并且执行。
具体使用
例子
我们先看一个例子:如下代码中,用sleep代替网络请求的耗时操作
1 | import 'dart:io'; |
输出结果为:
start
2秒钟之后输出
hello world
end
很显然阻塞了后面的代码执行
使用Future
Future
表示一件“将来”会发生的事情,将来可以从Future中取到一个值。
有了Future之后,通过.then的回调去获取请求到的结果
1 | import 'dart:io'; |
catchError
如果有异常的话,我们可以用catchError来捕获
1 | import 'dart:io'; |
输出为
start
end
Exception: 请求异常
Future的链式调用
1 | import 'dart:io'; |
输出
start
end
hello world
第一次调用完成
第二次调用完成
直接获取一个完成的Future,该Future会直接调用then的回调函数
1 | import 'dart:io'; |
输出为:
start
end
测试
我们可以看到,测试
是最后才打印,这是因为Future中的then会作为新的任务会加入到事件队列中(Event Queue),加入之后你需要排队执行。
总结
- 创建一个Future(可能是我们创建的,也可能是调用内部API或者第三方API获取到的一个Future,总之你需要获取到一个Future实例,Future通常会对一些异步的操作进行封装);
- 通过.then(成功回调函数)的方式来监听Future内部执行完成时获取到的结果;
- 通过.catchError(失败或异常回调函数)的方式来监听Future内部执行失败或者出现异常时的错误信息;
- 可以链式调用
- 直接获取一个完成的Future,该Future会直接调用then的回调函数
QQ交流群:592831498