可重复使用的同步 HttpClient 请求

Reusable Sync HttpClient Request

我需要一个可重复使用的函数来发出 HTTP 请求并等待其完成,然后再将响应作为字符串返回。

这是主要功能:

main() async {
  var json;
  json = await makeRequest('https://...');
  print(json);
  print('*** request complete ***');
}

(第一种情况) 这是发出 HTTP 请求的可重用函数:

makeRequest(String url) async {
  var request = await new HttpClient().postUrl(Uri.parse(url));

  // Includes the access token in the request headers.
  request.headers.add(...);

  // Waits until the request is complete.
  var response = await request.close();

  await for (var contents in response.transform(UTF8.decoder)) {
    return contents;
  }
}

这按预期工作,输出为:

// Response contents as a String...
*** request complete ***

(第二种情况)然后我尝试这样做但没有成功:

makeRequest(String url) async {
  var request = await new HttpClient().postUrl(Uri.parse(url));

  // Includes the access token in the request headers.
  request.headers.add(...);

  // Waits until the request is complete.
  var response = await request.close();

  var json = '';
  await response.transform(UTF8.decoder).listen((contents) {
    // At first I tried to return contents here, but then I added onDone().
    json += contents;
  }, onDone: () {
    return json;
  });

  return json;
}

我试过用 asyncawaitlisten 中定义函数,在没有 onDone() 的情况下在 listen 中返回 contents,但输出是一样的:

// Empty line.
*** request complete ***
// Waits a few seconds doing nothing before terminating...

有谁知道为什么第二种情况不起作用?

编辑:

更新代码后,它执行了它应该执行的操作,但在终止执行之前需要几秒钟:

Future<String> twitterRequest(String url) async {
  var request = await new HttpClient().postUrl(Uri.parse(url));

  // Includes the access token in the request headers.
  request.headers.add(...);

  // Waits until the request is complete.
  var response = await request.close();

  var json = '';
  await for (var contents in response.transform(UTF8.decoder)) {
    json += contents;
    // Putting a break here produces the same output but terminates immediately (as wanted).
  }

  return json;
}

输出:

// Prints response contents...
*** request complete ***
// Takes a few seconds before execution terminates. With the break the code terminates immediately.

编辑2:

提交this issue on GitHub后,我发现HttpClient的实例有一个连接池并默认保持持久连接,这使Dart VM保持活动状态。请查阅问题页面以了解可能的解决方案。

可能是response.transform之前的await造成的。

你可能想要像

这样的东西
  return response.transform(UTF8.decoder).join('');

暂停与makeRequest()无关。 Dart VM 似乎在退出前等待着什么。添加 exit(0); 作为 main() 中的最后一行使应用程序立即退出。

更新

根据 Dart SDK issue

上的回复

This is caused by the HttpClient instance having a connection pool which can keep the Dart VM alive. There are two ways of avoiding this:

1) Close the HttpClient explicitly
2) Use non-persistent connections

import 'dart:async';
import 'dart:convert' show UTF8;
import 'dart:io';

Future main() async {
  await makeRequest();
  print('end of main');
}

Future makeRequest() async {
  var client = new HttpClient();
  var request = await client.postUrl(Uri.parse('https://example.com'));
  var response = await request.close();
  var contents = await response.transform(UTF8.decoder).join();
  print(contents);
  client.close();  // Close the client.
}
import 'dart:async';
import 'dart:convert' show UTF8;
import 'dart:io';

Future main() async {
  await makeRequest();
  print('end of main');
}

Future makeRequest() async {
  var request = await new HttpClient().postUrl(Uri.parse('https://example.com'));
  request.persistentConnection = false;  // Use non-persistent connection.
  var response = await request.close();
  var contents = await response.transform(UTF8.decoder).join();
  print(contents);
}