likes
comments
collection
share

关于Dart中Future的一些理解

作者站长头像
站长
· 阅读数 2

最近在看Flutter,在实现网络请求时看到了Future,本以为和JS中的Promise一样,用起来还是有些不同的,网上的文章讲的不是很详细,自己就搜集了一些资料,下面是我自己的一些总结,帮助一些像我一样迷惑的人

Dart中的线程

Dart是单线程的,是可以异步的,Event Loop机制,iOS中叫runLoop,线程在大部分时间在等待被唤醒去做某些事情,在需要去响应某些操作的时候再去处理事情,例如用户的点击事件,网络请求的返回处理等等

异步任务

Dart中有两个队列,一个是EventQueue(事件队列) 另一个是MicrotaskQueue(微任务队列),Dart的Event Loop机制会不断的轮询事件队列,在每次的轮询中,Dart会先处理MicrotaskQueue,再处理EventQueue,这就产生了一个优先级的区别,微任务享有最高的优先级,微任务的通过scheduleMicrotask建立

scheduleMicrotask(() => print('This is a microtask'));

但是,直接用到微任务队列的情况很少(你要是想用也没人拦着);异步任务更多的情况是事件队列来完成,Dart为EventQueue做了封装,就是Future,用法如下

Future(() => print('Running in Future 1'));//下一个事件循环输出字符串

Future(() => print(‘Running in Future 2'))
  .then((_) => print('and then 1'))
  .then((_) => print('and then 2’));//上一个事件循环结束后,连续输出三段字符串

熟悉的配方,熟悉的感觉。。。是不是和Promise很像,但还是有些区别,这个时候就会有疑问,这个then是什么时候调用的?(Promise中可以主动调用resolve去触发)当我们声明一个Future时,Dart会将异步任务的函数执行体放入事件队列,然后返回,接着同步执行后续的代码;当同步执行完成后,事件队列会按照声明同步执行Future的函数体和后续的then 这里还有个特殊情况,就是当Future的执行体返回值为null时后面又有个.then时,Dart就会把then放入微任务队列,示例代码如下

//f1比f2先执行
Future(() => print('f1'));
Future(() => print('f2'));

//f3执行后会立刻同步执行then 3
Future(() => print('f3')).then((_) => print('then 3'));

//then 4会加入微任务队列,尽快执行
Future(() => null).then((_) => print('then 4'));

then会在Future函数体执行完毕后立刻执行,再看个比较复杂的例子


Future(() => print('f1'));//声明一个匿名Future
Future fx = Future(() =>  null);//声明Future fx,其执行体为null

//声明一个匿名Future,并注册了两个then。在第一个then回调里启动了一个微任务
Future(() => print('f2')).then((_) {
  print('f3');
  scheduleMicrotask(() => print('f4'));
}).then((_) => print('f5'));

//声明了一个匿名Future,并注册了两个then。第一个then是一个Future
Future(() => print('f6'))
  .then((_) => Future(() => print('f7')))
  .then((_) => print('f8'));

//声明了一个匿名Future
Future(() => print('f9'));

//往执行体为null的fx注册了了一个then
fx.then((_) => print('f10'));

//启动一个微任务
scheduleMicrotask(() => print('f11'));
print('f12');

打印顺序为 f12 f11 f1 f10 f2 f3 f5 f4 f6 f9 f7 f8 把这个例子弄懂了Future是怎么使用应该就会了;

因为其他语句都是异步任务,所以先打印 f12。剩下的异步任务中,微任务队列优先级最高,因此随后打印 f11;然后按照 Future 声明的先后顺序,打印 f1。随后到了 fx,由于 fx 的执行体是 null,相当于执行完毕了,Dart 将 fx 的 then 放入微任务队列,由于微任务队列的优先级最高,因此 fx 的 then 还是会最先执行,打印 f10。然后到了 fx 下面的 f2,打印 f2,然后执行 then,打印 f3。f4 是一个微任务,要到下一个事件循环才执行,因此后续的 then 继续同步执行,打印 f5。本次事件循环结束,下一个事件循环取出 f4 这个微任务,打印 f4。然后到了 f2 下面的 f6,打印 f6,然后执行 then。这里需要注意的是,这个 then 是一个 Future 异步任务,因此这个 then,以及后续的 then 都被放入到事件队列中了。f6 下面还有 f9,打印 f9。最后一个事件循环,打印 f7,以及后续的 f8。

异步函数

关于Future的处理有两种方式,使用.then,等Future的执行体结束了再异步处理;或者使用async和await来同步等待执行体完成,下面是使用方法

//声明了一个延迟3秒返回Hello的Future,并注册了一个then返回拼接后的Hello 2020
Future<String> fetchContent() => 
  Future<String>.delayed(Duration(seconds:3), () => "Hello")
    .then((x) => "$x 2020");

  main() async{
    print(await fetchContent());//等待Hello 2020的返回
  }

使用async关键字的函数也是一个异步函数,这个await类似js中的async,await;看下面的代码

Future(() => print('f1'))
  .then((_) async {
    print('f5');
    await Future(() => print('f2'));
  })
  .then((_) => print('f3'));
Future(() => print('f4'));

打印出来的结果是 f1、f5、f4、f2、f3;先打印f1,之后同步执行.then;但是.then中的执行体使用了async,它是个异步函数,先执行f5,之后f2被放入了事件队列,所以首先打印f4,之后是f2,最后是f3; 本文参考了极客时间的Flutter核心技术与实战 time.geekbang.org/column/intr… 这个是我目前在网上找到的对Future解释最清楚的了,有些不对的地方还请大佬指正