阅读 992

还没搞懂async和await?看完这篇还不懂的话你来静步🔪我

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

前言:最近在帮一些朋友改Bug的时候,总是会有一些奇怪的问题(说实话给我整懵了),仔细排查后发现是语法写错了,说多了都是泪,当初入门Flutter时,发现Dart语法与Java很相似,就没有仔细学习Dart的语法,现在这基础不扎实的弊端就体现出来了😖

(对于使用Js和Dart的程序员都建议仔细阅读这篇文章,因为都是单线程的语言)

什么是异步,理解这个很重要!!

简单解释

同步一般指两个任务之间执行次序有先后之分

异步一般指同时做多件事(我就问你下面这张图形象吗👍)

异步.gif

常见的一些异步操作:

1)网络请求时

2)写入数据库时

3)从文件读取数据时

Dart中的异步

这里给了我们两个实现异步逻辑的方法

官方:

这里我们详解Future

使用Future

一般常用的Future构造函数:

所以代码都可以在这里运行:dartpad.dev/

new Future((){
    // 这里写业务代码
});
复制代码

当然还有分治的任务,需要将一个大任务拆成很多小任务一步一步执行,需要使用Future.then函数来拆解任务

new Future(() => 自定义的函数)  //  异步任务的函数
        .then((a) => "返回:$a")  //   任务执行完后的子任务
        .then((a) => a.length)  //  其中a为上个任务执行完后的返回的结果
        .then((a) => printLength(a))
        .whenComplete(() => whenTaskCompelete);  //  当所有任务完成后的回调函数
}
复制代码

当任务需要延迟执行时,可以使用new Future.delay来将任务延迟执行

Future.delayed(const Duration(seconds: 2), () => print(''));
复制代码

举一个简单的例子:

void main() {
  print('开始运行');
  getUserMessage();
}
​
Future<void> getUserMessage() {
  //假设我们在请求一些数据
  return Future.delayed(const Duration(seconds: 2), () => print('获取到网络的信息'));
}
复制代码

结果应该是:

开始运行
//过两秒后
获取到网络的信息
复制代码

使用 async-await

这是实现异步逻辑的一种语法

记住两个准则:

记住以下两个基本准则:

  • 要定义异步函数,请在函数体之前添加async:
  • await关键字仅在async函数中起作用。

在同步函数的基础上给一个简单的例子:

void main() {
  print('开始运行');
  getUserMessage();
}
​
Future<void> getUserMessage() async{
  print(await postUserInput());
  //假设我们在请求一些数据
  return Future.delayed(const Duration(seconds: 2), () => print('获取到网络的信息'));
}
​
//假设我们在发送一些数据,当然正常情况下,发送数据也是异步的
Future<String> postUserInput() =>
    Future.delayed(
      const Duration(seconds: 2),
      () => '我发送了数据',
    );
复制代码

返回结果是正确的:

开始运行
我发送了数据
获取到网络的信息
复制代码

但是如果没有使用await

 print(postUserInput());
复制代码

这个时候就会打印出:

开始运行
Instance of '_Future<String>'
获取到网络的信息
复制代码

为什么呢?

解析:

  • postUserInput() 是一个异步函数,它在延迟后打印“我发送了数据”
  • 正确的情况应该是等待postUserInput()运行完成再请求数据,但是这里没有使用await,就是没有得到返回的字符串
  • 所以这里无法正确的描述postUserInput(),只能返回它是一个Future的实例

如果使用把这个函数赋值给变量是会报错的

String data = postUserInput();
复制代码

解析与上面相同

顺序处理

你可以使用多个 await 表达式来确保各个语句在执行下一个语句之前完成:

// 使用关键字 async 和 await 顺序处理异步函数逻辑
main() async {
  await A();
  await B();
  test(await C());
}
复制代码

async-await总结:

  1. 要想 return await … ,那么函数首先是 async 的
  2. 使用 async 标注的函数或者是异步的函数,必须要用 await 接收返回值,如:var data = await postUserInput();

使用then:

then回调:(在完成请求功能后不想停留直接运行下一个代码块)

Future<R> then <R>(
    FutureOr<R> onValue(
    T value
    ), {
    Function onError
})  
复制代码

举一个简单的例子:

void main() {
  print('开始运行');
  Future.delayed(Duration(seconds: 1), () {
    print('请求数据');
  }).then((a) {
    print('获取到的数据$a');
  });
}
复制代码

输出:

开始运行
请求数据
获取到的数据null
复制代码

then与await的区别总结:

then的回调类似于观察者模式,then主要目的是在Future完成的时候再来处理value从而避免阻塞,而await会暂停所在的async函数的执行,对于堆栈来说这个不同点十分关键!!

处理异常错误

处理async中的错误需要使用try

Future<void> printDailyNewsDigest() async {
  try {
    var newsDigest = await gatherNewsReports();
    print(newsDigest);
  } catch (e) {
    // 处理代码执行错误...
  }
}
复制代码

使用catchError与whenComplete

Future还有两个比较常用的api,catchError、whenComplete。 catchError用于捕捉错误,whenComplete在什么情况都会调用,

一般来说,如果需要监听“结束”这个状态,那么用whenComplete,需要监听“成功”这个状态,用then,需要监听“失败”这个状态,用catchError,就是这么滴简单~

catchError方法和then方法是同一级别的,catchError是最后的保障,catchError与then方法的onError参数不同的是,catchError可以处理之前所有处理过程中产生的error,而onError只能够处理调用then的Future对象的异常

Future.then((value)=>getUserMessage(value), onError: (e){}).then().catchError();  
复制代码

在这里,onError只能够捕获Future的异常,但是catchError可以捕获Future和getUserMessage中产生的异常。

文字不多,但是都是用心总结的,所以,兄弟们给个赞呗,如果哪里说的不完整或者错了,在评论区告诉我,感谢~

有问题的可以在这里联系我

文章分类
前端