【问题标题】:Basic Async/Await pattern?基本的异步/等待模式?
【发布时间】:2019-02-16 13:05:55
【问题描述】:

我正在尝试找出 Dart 中的异步/等待问题。我正在调用一个简短的 http 获取序列,并希望等待它们。我创建了这个示例代码来演示这个问题。

我希望看到“开始测试”,然后是每个下载按顺序开始。然后每个下载都会以随机顺序完成,然后我会看到“结束测试”。但是,在任何下载开始之前,我都会看到“结束测试”。

请帮助我理解我做错了什么。

import 'dart:async';
import 'package:http/http.dart' as http;

class AsyncTest {
  final _resourceList = [
    {
      'name': 'Test 1',
      'url': 'https://jsonplaceholder.typicode.com/posts'
    },
    {
      'name': 'Test 2',
      'url': 'https://jsonplaceholder.typicode.com/comments'
    },
    {
      'name': 'Test 3',
      'url': 'https://jsonplaceholder.typicode.com/albums'
    },
    {
      'name': 'Test 4',
      'url': 'https://jsonplaceholder.typicode.com/photos'
    },
    {
      'name': 'Test 5',
      'url': 'https://jsonplaceholder.typicode.com/users'
    },
  ];

  void beginTest() async {
    print('begin test');
    await getAll();
    print('end test');
  }

  Future<void> getAll() async {
    _resourceList.forEach((resource) async {
      print('begin ${resource['name']}');

      final response = await http.get(resource['url']);

      print('end ${resource['name']}');
    });
  }
}

这是输出。

flutter: begin test
flutter: end test
flutter: begin Test 1
flutter: begin Test 2
flutter: begin Test 3
flutter: begin Test 4
flutter: begin Test 5
flutter: end Test 1
flutter: end Test 5
flutter: end Test 3
flutter: end Test 2
flutter: end Test 4

【问题讨论】:

    标签: dart


    【解决方案1】:

    我希望在每次下载后看到“开始测试” 按顺序开始。然后每个下载将随机完成 订单,然后我会看到“结束测试”。但是我看到“结束测试” 在任何下载开始之前。

    那是因为您(意外地)要求异步调用发生在之后 'end test':

    Future<void> getAll() async {
      _resourceList.forEach((resource) async {
        print('begin ${resource['name']}');
    
        final response = await http.get(resource['url']);
    
        print('end ${resource['name']}');
      });
    }
    

    具体来说,你的功能是:

    1. 输入getAll() function
    2. 调用_resourceList.forEach(...)
    3. 屈服于微任务循环
    4. 激动人心的getAll()函数
    5. 在下一个微任务中,执行async { ... } 回调的主体

    您有几个选择。如果您想使用回调,您可以使用可能的Future.waitFuture.forEach,否则for ... 会在退出getAll() 之前执行您想要的操作 - 内置的for ... loop will be transformed to completing the Future when you expect

    Future<void> getAll() async {
      for (var resource in _resourceList) {
        print('begin ${resource['name']}');
    
        final response = await http.get(resource['url']);
    
        print('end ${resource['name']}');
      }
    }
    

    【讨论】:

    • 谢谢。帮助我理解为什么。在您的解释步骤 3“屈服于微任务循环”中,我在哪里做这个?还是 List.forEach 本质上会产生?我已经实现了您使用常规 for 循环的建议,它确实达到了我的预期。我接受这个答案。
    【解决方案2】:

    您在getAll() 中的await 调用允许事件循环处理不同的路径。所以是的,所有早期的事情(开始......)首先出现,而后来的事情(结束......)以明显随机的顺序出现是有道理的。这是异步编程的内在本质。

    【讨论】:

    • 那部分是我所期望和希望发生的。提取是独立的,基本上是并行发生的。然而,“结束测试”发生在每个单独的测试结束之前。我想确保我的“await getAll()”实际上一直等到完成。
    【解决方案3】:

    forEach(callback) 忽略来自callback 的返回值。

    如果您使用for 循环,await 将按预期工作:

      Future<void> getAll() async {
        for(var resource in _resourceList) {
          print('begin ${resource['name']}');
    
          final response = await http.get(resource['url']);
    
          print('end ${resource['name']}');
        }
      }
    

    或者让循环体同时执行,你可以使用

      Future<void> getAll() async {
        return Future.wait(_resourceList.map((resource) async {
          print('begin ${resource['name']}');
    
          final response = await http.get(resource['url']);
    
          print('end ${resource['name']}');
        }));
      }
    

    使用map,从回调返回的Futures 被收集,当它们全部完成(成功或错误)时,返回的Future 将完成。

    【讨论】:

      猜你喜欢
      • 2016-12-07
      • 1970-01-01
      • 1970-01-01
      • 2017-12-02
      • 1970-01-01
      • 2019-11-17
      • 2016-04-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多