美文网首页
Dart 笔记 12 - 异步

Dart 笔记 12 - 异步

作者: 三流之路 | 来源:发表于2019-01-18 17:24 被阅读0次

    在 dart:async 库中,要先 import。

    Future

    delayed 创建一个延时任务。

    Future.delayed(new Duration(seconds: 2),(){
       return "hi world!";
    }).then((data){
       print(data);
    });
    

    String lookUpVersion() => '1.0.0';
    

    如果将其改变成一个异步函数,返回值将是一个 Future,异步函数要有 async 关键字:

    Future<String> lookUpVersion() async => '1.0.0';
    

    如果函数没有返回一个有用的值,那么返回 Future<void> 类型。

    设有三个异步函数

    Future<String> login(String userName, String pwd) async {
        //用户登录
        return '$userName-pwd'
    }
    
    Future<String> getUserInfo(String id) async {
        //获取用户信息 
        return 'user id $id'
    }
    
    // 保存用户信息 
    Future<void> saveUserInfo(String userInfo) async => print(userInfo)
    
    void main() {
      login("alice","1234").then((id) {
        // 登录成功后通过,id 指代返回的值   
        getUserInfo(id).then((userInfo) {
          // 获取用户信息后保存,无返回值时参数表示异常
          saveUserInfo(userInfo).then((e){
            // 保存用户信息,接下来执行其它操作
          });
        });
      });
    }
    

    结果输出 user id alice-pwd

    链接多个异步函数

    如果有大量的异步函数,像上面那样就会产生回调地狱。首先可以通过链式调用避免

    void main() {
      login("alice","1234").then((id) {
        return getUserInfo(id);
      }).then((userInfo) => saveUserInfo(userInfo)
      ).then((e){
            
      }).catchError((e) { 
        // 使用 catchError() 来处理 Future 对象可能抛出的任何错误或异常
      });
    }
    

    then().catchError() 模式是 try-catch 的异步版本。

    一定要在 then() 的结果上调用 catchError(),而不是在原始 Future 的结果上。否则,catchError() 只能从原始 Future 的计算中处理错误,而不能从 then() 注册的处理程序处理错误。

    使用 await

    还可以用 await 改造,使异步代码看起来像同步一样。

    await 必须在 async 标注的异步函数中调用,返回一个 Future 对象。await 表达式会让程序执行挂起,直到返回的对象可用。

    void main() async {
      try { // 可以捕获异常
        var id = await login("alice","1234");
        var userInfo = await getUserInfo(id);
        await saveUserInfo(userInfo);
      } catch (e) {
    
      } 
    }
    

    等待多个 Future

    有时需要调用许多异步函数,并等待它们全部完成后再继续。使用 Future.wait() 静态方法管理多个 Future,并等待它们完成:

    Future deleteLotsOfFiles() async =>  ...
    Future copyLotsOfFiles() async =>  ...
    Future checksumLotsOfOtherFiles() async =>  ...
    
    await Future.wait([
      deleteLotsOfFiles(),
      copyLotsOfFiles(),
      checksumLotsOfOtherFiles(),
    ]);
    
    print('Done with all the long steps!');
    

    onError, whenComplete

    then 有一个可选参数 onError,可以处理异常。

    Future.delayed(new Duration(seconds: 2), () {
        //return "hi world!";
        throw AssertionError("Error");
    }).then((data) {
        print("success");
    }, onError: (e) {
        print(e);
    });
    

    无论异步任务执行成功或失败都要做的事,在 Future 的 whenComplete 回调里执行

    Future.delayed(new Duration(seconds: 2),(){
       throw AssertionError("Error");
    }).then((data){
       // 执行成功会走到这里 
       print(data);
    }).catchError((e){
       // 执行失败会走到这里   
       print(e);
    }).whenComplete((){
       // 无论成功或失败都会走到这里
    });
    

    Stream

    也是用于接收异步事件数据,和 Future 不同的是,它可以接收多个异步操作的结果(成功或失败)。也就是说,在执行异步任务时,可以通过多次触发成功或失败事件来传递结果数据或错误异常。 Stream 常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。

    Stream.fromFutures([
      // 1秒后返回结果
      Future.delayed(new Duration(seconds: 1), () {
        return "hello 1";
      }),
      // 抛出一个异常
      Future.delayed(new Duration(seconds: 2),(){
        throw AssertionError("Error");
      }),
      // 3秒后返回结果
      Future.delayed(new Duration(seconds: 3), () {
        return "hello 3";
      })
    ]).listen((data){
       print(data);
    }, onError: (e){
       print(e.message);
    },onDone: (){
    
    });
    

    结果

    I/flutter (17666): hello 1
    I/flutter (17666): Error
    I/flutter (17666): hello 3
    

    使用异步 for 循环

    有时可以使用异步 for 循环(wait for),而不是使用流 API。

    下面的函数使用 Stream 的 listen() 方法订阅一个文件列表,传入一个搜索每个文件或目录的函数文字。

    void main(List<String> arguments) {
      // ...
      FileSystemEntity.isDirectory(searchPath).then((isDir) {
        if (isDir) {
          final startingDir = Directory(searchPath);
          startingDir
              .list(
                  recursive: argResults[recursive],
                  followLinks: argResults[followLinks])
              .listen((entity) {
            if (entity is File) {
              searchFile(entity, searchTerms);
            }
          });
        } else {
          searchFile(File(searchPath), searchTerms);
        }
      });
    }
    

    带有 await 表达式的等价代码,包括异步 for 循环(await for),看起来更像同步代码:

    Future main(List<String> arguments) async {
      // ...
      if (await FileSystemEntity.isDirectory(searchPath)) {
        final startingDir = Directory(searchPath);
        await for (var entity in startingDir.list(
            recursive: argResults[recursive],
            followLinks: argResults[followLinks])) {
          if (entity is File) {
            searchFile(entity, searchTerms);
          }
        }
      } else {
        searchFile(File(searchPath), searchTerms);
      }
    }
    

    监听流数据

    要在每个值到达时获得它,可以使用 await() 方法对流使用或使用 listen() 方法订阅:

    // Find a button by ID and add an event handler.
    querySelector('#submitInfo').onClick.listen((e) {
      // When the button is clicked, it runs this code.
      submitData();
    });
    

    onClick 属性是 “submitInfo” 按钮提供的流对象。

    如果只关心一个事件,那么可以使用属性 first、last 或 single 来获得它。要在处理事件之前测试它,可以使用诸如 firstWhere()、lastWhere() 或 singleWhere() 之类的方法。

    如果关心事件的子集,可以使用诸如 skip()、skipWhile()、take()、takeWhile() 和 where() 等方法。

    改变流数据

    通常需要在使用流数据之前更改其格式。使用 transform() 方法生成具有不同类型数据的流:

    var lines = inputStream
        .transform(utf8.decoder)
        .transform(LineSplitter());
    

    首先使用 utf8.decoder 将整数流转换为字符串流。然后使用 LineSplitter 将字符串流转换为单独的行流。这些转换器来自 dart:convert 库。

    处理错误和完成

    如何指定错误和完成处理代码取决于是使用异步 for 循环(wait for)还是流 API。

    如果使用异步 for 循环,则使用 try-catch 处理错误。在流关闭后执行的代码在异步 for 循环之后执行。

    Future readFileAwaitFor() async {
      var config = File('config.txt');
      Stream<List<int>> inputStream = config.openRead();
    
      var lines = inputStream
          .transform(utf8.decoder)
          .transform(LineSplitter());
      try {
        await for (var line in lines) {
          print('Got ${line.length} characters from stream');
        }
        print('file is now closed');
      } catch (e) {
        print(e);
      }
    }
    

    如果使用流 API,则通过注册 onError 侦听器来处理错误。通过注册 onDone 侦听器,在流关闭后运行代码。

    var config = File('config.txt');
    Stream<List<int>> inputStream = config.openRead();
    
    inputStream
        .transform(utf8.decoder)
        .transform(LineSplitter())
        .listen((String line) {
      print('Got ${line.length} characters from stream');
    }, onDone: () {
      print('file is now closed');
    }, onError: (e) {
      print(e);
    });
    

    可以使用 break 或 return 语句,该语句将跳出 for 循环,并从流中取消订阅。

    相关文章

      网友评论

          本文标题:Dart 笔记 12 - 异步

          本文链接:https://www.haomeiwen.com/subject/uiandqtx.html