Alfred框架异步处理函数执行顺序错乱,如何实现预期执行逻辑?
问题原因
Dart 中的 Timer.periodic 属于非阻塞异步 API,调用后会立即返回,不会等待定时器回调内的异步逻辑执行完成。虽然你给 complexTendexAPIRequest 加了 async 修饰,但函数在创建完定时器后就直接结束返回了,外层的 await complexTendexAPIRequest(...) 并没有真正等待轮询、拉取结果、写文件的完整流程执行完成,因此会出现输出顺序错乱的问题。
修复方案
方案1:用 Completer 封装 Timer 逻辑(保留 Timer 实现)
可以通过 Completer 包装整个轮询流程,只有当所有操作执行完成后再标记流程结束,外层的 await 就能正常等待整个函数执行完成:
import 'dart:async'; // 明确声明返回 Future<void> Future<void> complexTendexAPIRequest(Map<String, dynamic> data) async { print('Request: $data'); final completer = Completer<void>(); try { final response = await http.post( Uri.parse(COMPLEX_URL), headers: {'Content-Type': 'application/json', 'Authorization': 'bearer $ACCESS_TOKEN'}, body: json.encode(data) ); if(response.statusCode == 200) { var res = json.decode(response.body); int latestId = res['id']; String url = 'https://api.ru/v2/complex/status?id=$latestId'; stdout.write('Waiting for "complete" status from API: '); Timer.periodic(Duration(seconds: 1), (timer) async { try { final response = await http.get( Uri.parse(url), headers: {'Content-Type': 'application/json', 'Authorization': 'bearer $ACCESS_TOKEN'} ); var data = json.decode(response.body); if(data['status'] == 'completed') { timer.cancel(); stdout.write('[DONE]'); stdout.write('\nFetching result: '); String url = "https://api.ru/v2/complex/results?id=$latestId"; final response = await http.get( Uri.parse(url), headers: {'Content-Type': 'application/json', 'Authorization': 'bearer $ACCESS_TOKEN'} ); stdout.write('[DONE]'); var data = prettyJson(json.decode(response.body)); await File('result.json').writeAsString(data.toString()); print("\nCreating dump of result: [DONE]"); // 所有逻辑执行完成,标记结束 completer.complete(); } } catch(e) { timer.cancel(); // 轮询过程出现异常,向外抛出 completer.completeError(e); } }); } else { print('[ERROR] Wrong status code for complex request. StatusCode: ${response.statusCode}'); completer.complete(); } } on SocketException catch(e) { print('No Internet connection: $e'); completer.completeError(e); } on TimeoutException catch(e) { print('TenderAPI Timeout: $e'); completer.completeError(e); } on Exception catch(e) { print('Some unknown Exception: $e'); completer.completeError(e); } // 返回 Completer 的 Future 供外层 await return completer.future; }
方案2:改用 while 循环实现轮询(更简洁的方案)
这种方案不需要额外引入 Completer,本身就是顺序执行的异步逻辑,外层 await 可以直接等待整个流程结束,也是你当前已经验证可行的方案:
Future<void> complexTendexAPIRequest(Map<String, dynamic> data) async { print('Request: $data'); try { final response = await http.post( Uri.parse(COMPLEX_URL), headers: {'Content-Type': 'application/json', 'Authorization': 'bearer $ACCESS_TOKEN'}, body: json.encode(data) ); if(response.statusCode == 200) { var res = json.decode(response.body); int latestId = res['id']; String statusUrl = 'https://api.ru/v2/complex/status?id=$latestId'; stdout.write('Waiting for "complete" status from API: '); // while 循环轮询状态 while(true) { final response = await http.get( Uri.parse(statusUrl), headers: {'Content-Type': 'application/json', 'Authorization': 'bearer $ACCESS_TOKEN'} ); var statusData = json.decode(response.body); if(statusData['status'] == 'completed') { break; } // 每秒轮询一次 await Future.delayed(Duration(seconds: 1)); } stdout.write('[DONE]'); stdout.write('\nFetching result: '); String resultUrl = "https://api.ru/v2/complex/results?id=$latestId"; final resultResponse = await http.get( Uri.parse(resultUrl), headers: {'Content-Type': 'application/json', 'Authorization': 'bearer $ACCESS_TOKEN'} ); stdout.write('[DONE]'); var resultData = prettyJson(json.decode(resultResponse.body)); await File('result.json').writeAsString(resultData.toString()); print("\nCreating dump of result: [DONE]"); } else { print('[ERROR] Wrong status code for complex request. StatusCode: ${response.statusCode}'); } } on SocketException catch(e) { print('No Internet connection: $e'); rethrow; } on TimeoutException catch(e) { print('TenderAPI Timeout: $e'); rethrow; } on Exception catch(e) { print('Some unknown Exception: $e'); rethrow; } }
以上两种方案都可以保证 print('Hello World') 和 await res.json({'data': 'ok'}) 会在 complexTendexAPIRequest 所有逻辑执行完成后再运行。
内容的提问来源于stack exchange,提问作者Dmitry Bubnenkov
相关产品推荐
相关产品推荐

