flutter的dart:async 异步 、dart:math 数学 、dart:convert 转换、dart:io、JavaScript 互作性

发布于:2025-06-25 ⋅ 阅读:(19) ⋅ 点赞:(0)

dart:async  dart:异步

异步编程通常使用回调函数,但 Dart 提供了替代方案:Future 和 Stream 对象。Future 就像承诺在未来的某个时候提供结果。Stream 是一种获取值序列(如事件)的方法。Future、Stream 等都在 dart:async 库(API 参考 )中。

信息提示

您并不总是需要直接使用 Future 或 Stream API。Dart 语言支持使用 async 和 await 等关键字进行异步编码。查看异步编程教程 了解详情。

dart:async 库适用于 Web 应用程序和命令行应用程序。要使用它,请导入 dart:async:

import 'dart:async';

灯泡小提示

你不需要导入 dart:async 来使用 Future 和 Stream API,因为 dart:core 会导出这些类。

Future  未来

#

Future 对象出现在 Dart 库中,通常作为异步方法返回的对象。当 future 完成时,其值即可使用。

Using await  使用 await

#

在直接使用 Future API 之前,请考虑改用 await。使用 await 表达式的代码比使用 Future API 的代码更容易理解。

void runUsingFuture() {
  // ...
  findEntryPoint()
      .then((entryPoint) {
        return runExecutable(entryPoint, args);
      })
      .then(flushThenExit);
}

带有 await 表达式的等效代码看起来更像同步代码:

Future<void> runUsingAsyncAwait() async {
  // ...
  var entryPoint = await findEntryPoint();
  var exitCode = await runExecutable(entryPoint, args);
  await flushThenExit(exitCode);
}

async 函数可以从 Future 中捕获异常。例如:

var entryPoint = await findEntryPoint();
try {
  var exitCode = await runExecutable(entryPoint, args);
  await flushThenExit(exitCode);
} catch (e) {
  // Handle the error...
}

反馈重点提醒

异步函数返回 Futures。如果您不希望函数返回 future,请使用其他解决方案。例如,您可以将 async function 从您的函数中获取。

Basic usage  基本用法

#

您可以使用 then() 来安排在 future 完成时运行的代码。例如,Client.read() 返回一个 Future,因为 HTTP 请求可能需要一段时间。使用 then() 可以在 Future 完成并且承诺的字符串值可用时运行一些代码:

httpClient.read(url).then((String result) {
  print(result);
});

使用 catchError() 来处理 Future 对象可能引发的任何错误或异常。

httpClient
    .read(url)
    .then((String result) {
      print(result);
    })
    .catchError((e) {
      // Handle or ignore the error.
    });

then().catchError() 模式是 try-catch 的 Catch 方法。

反馈重点提醒

确保在 then() 的结果上调用 catchError(),而不是在原始 Future 的结果上。否则, catchError() 只能处理来自原始 Future 计算的错误,而不能处理来自 then() 注册的处理程序的错误。

Chaining multiple asynchronous methods
链接多个异步方法

then() 方法返回一个 Future,提供了一种按特定顺序运行多个异步函数的有用方法。如果使用 then() 注册的回调返回 Future, then() 返回一个 Future ,该 Future 将完成 与回调返回的 Future 结果相同。 如果回调返回任何其他类型的值,then() 会创建一个新的 Future,该 Future 以该值结束。

Future result = costlyQuery(url);
result
    .then((value) => expensiveWork(value))
    .then((_) => lengthyComputation())
    .then((_) => print('Done!'))
    .catchError((exception) {
      /* Handle exception... */
    });

在前面的示例中,方法按以下顺序运行:

  1. costlyQuery()  costlyQuery()
  2. expensiveWork()  expensiveWork()
  3. lengthyComputation()  lengthyComputation()

以下是使用 await 编写的相同代码:

try {
  final value = await costlyQuery(url);
  await expensiveWork(value);
  await lengthyComputation();
  print('Done!');
} catch (e) {
  /* Handle exception... */
}

Waiting for multiple futures
等待多个 futures

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

Future<void> deleteLotsOfFiles() async =>  ...
Future<void> copyLotsOfFiles() async =>  ...
Future<void> checksumLotsOfOtherFiles() async =>  ...

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

Future.wait() 返回一个 future,一旦所有提供的 futures 都完成,它就会完成。它要么以他们的结果完成,要么如果提供的任何 futures 失败,则以错误完成。

Handling errors for multiple futures
处理多个 future 的错误

您还可以等待以下 Parallel Operations 执行:

这些扩展返回一个 Future 以及所有提供的 future 的结果值。与 Future.wait 不同,它们还允许你处理错误。

如果集合中的任何 future 完成并出现错误,则 wait 完成,并显示 ParallelWaitError。这允许调用方处理单个错误,并在必要时处理成功的结果。

当你不需要每个 future 的结果值时,对 future 的可迭代对象使用 wait

Future<int> delete() async => ...;
Future<String> copy() async => ...;
Future<bool> errorResult() async => ...;

void main() async {
  try {
    // Wait for each future in a list, returns a list of futures:
    var results = await [delete(), copy(), errorResult()].wait;
  } on ParallelWaitError<List<bool?>, List<AsyncError?>> catch (e) {
    print(e.values[0]);    // Prints successful future
    print(e.values[1]);    // Prints successful future
    print(e.values[2]);    // Prints null when the result is an error

    print(e.errors[0]);    // Prints null when the result is successful
    print(e.errors[1]);    // Prints null when the result is successful
    print(e.errors[2]);    // Prints error
  }
}

当您确实需要每个 future 的单个 result 值时,请对 future 的记录使用 wait。这提供了额外的好处,即 futures 可以是不同的类型:

Future<int> delete() async => ...;
Future<String> copy() async => ...;
Future<bool> errorResult() async => ...;

void main() async {
  try {
    // Wait for each future in a record.
    // Returns a record of futures that you can destructure.
    final (deleteInt, copyString, errorBool) =
        await (delete(), copy(), errorResult()).wait;

    // Do something with the results...
  } on ParallelWaitError<
    (int?, String?, bool?),
    (AsyncError?, AsyncError?, AsyncError?)
  > catch (e) {
    // ...
  }
}

Stream  流

#

Stream 对象出现在 Dart API 中,代表数据序列。例如,HTML 事件(如按钮单击)是使用流传递的。您还可以将文件作为流读取。

Using an asynchronous for loop
使用异步 for 循环

有时,您可以使用异步 for 循环 (await for) 而不是使用 Stream API。

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

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

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

void main(List<String> arguments) async {
  // ...
  if (await FileSystemEntity.isDirectory(searchPath)) {
    final startingDir = Directory(searchPath);
    await for (final entity in startingDir.list()) {
      if (entity is File) {
        searchFile(entity, searchTerms);
      }
    }
  } else {
    searchFile(File(searchPath), searchTerms);
  }
}

反馈重点提醒

在使用 await for 之前,请确保它使代码更清晰,并且您确实希望等待所有流的结果。例如,您通常不应该对 DOM 事件侦听器使用 await,因为 DOM 会发送无穷无尽的事件流。如果使用 await for 在一行中注册两个 DOM 事件侦听器,则永远不会处理第二种类型的事件。

有关使用 await 和相关 Dart 语言功能,请参阅 异步编程教程 

Listening for stream data
侦听流数据

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

// Add an event handler to a button.
submitButton.onClick.listen((e) {
  // When the button is clicked, it runs this code.
  submitData();
});

在此示例中,onClick 属性是提交按钮提供的 Stream 对象。

如果您只关心一个事件,则可以使用 firstlast 或 single 等属性来获取它。要在处理事件之前对其进行测试,请使用 firstWhere()、lastWhere() 或 singleWhere() 等方法。

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

Transforming stream data
转换流数据

通常,您需要先更改流数据的格式,然后才能使用它。使用 transform() 方法生成具有不同数据类型的流:

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

此示例使用两个变压器。首先,它使用 utf8.decoder 来 将整数流转换为字符串流。然后它使用 LineSplitter 将字符串流转换为 分隔行。这些转换器来自 dart:convert 库(参见 dart:convert 部分 )。

Handling errors and completion
处理错误和完成

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

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

Future<void> readFileAwaitFor() async {
  var config = File('config.txt');
  Stream<List<int>> inputStream = config.openRead();

  var lines = inputStream
      .transform(utf8.decoder)
      .transform(const LineSplitter());
  try {
    await for (final line in lines) {
      print('Got ${line.length} characters from stream');
    }
    print('file is now closed');
  } catch (e) {
    print(e);
  }
}

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

var config = File('config.txt');
Stream<List<int>> inputStream = config.openRead();

inputStream
    .transform(utf8.decoder)
    .transform(const LineSplitter())
    .listen(
      (String line) {
        print('Got ${line.length} characters from stream');
      },
      onDone: () {
        print('file is now closed');
      },
      onError: (e) {
        print(e);
      },
    );

More information  更多信息

#

有关在命令行应用程序中使用 Future 和 Stream 的一些示例,请查看 dart:io 文档 。另请参阅以下文章和教程:

dart:math  dart:数学

 dart:math 库(API 参考 )提供了常见的功能,如正弦和余弦、最大值和最小值,以及常数,如 pi 和 e。Math 库中的大多数功能都是作为顶级函数实现的。

要在你的应用程序中使用此库,请导入 dart:math。

import 'dart:math';

Trigonometry  三角法


Math 库提供基本的三角函数:

// Cosine
assert(cos(pi) == -1.0);

// Sine
var degrees = 30;
var radians = degrees * (pi / 180);
// radians is now 0.52359.
var sinOf30degrees = sin(radians);
// sin 30° = 0.5
assert((sinOf30degrees - 0.5).abs() < 0.01);

信息提示

这些函数使用弧度,而不是度数!

Maximum and minimum  最大值和最小值

#

Math 库提供了 max() 和 min() 方法:

assert(max(1, 1000) == 1000);
assert(min(1, -1000) == -1000);

Math constants  数学常数

#

在 Math 库中找到您最喜欢的常数 — pie 等:

// See the Math library for additional constants.
print(e); // 2.718281828459045
print(pi); // 3.141592653589793
print(sqrt2); // 1.4142135623730951

Random numbers  随机数

#

使用 Random 类生成随机数。您可以选择为 Random 构造函数提供种子。

var random = Random();
random.nextDouble(); // Between 0.0 and 1.0: [0, 1)
random.nextInt(10); // Between 0 and 9.

您甚至可以生成随机布尔值:

var random = Random();
random.nextBool(); // true or false

警告请注意

Random 的默认实现提供了不适合加密目的的伪随机位流。要创建加密安全的随机数生成器,请使用 Random.secure()构造函数。

dart:convert  dart:转换

dart:convert 库(API 参考 )具有 JSON 和 UTF-8 的转换器,并支持创建额外的转换器。JSON 是一种简单的文本格式,用于表示结构化对象和集合。UTF-8 是一种常见的可变宽度编码,可以表示 Unicode 字符集中的每个字符。

要使用此库,请导入 dart:convert。

import 'dart:convert';

Decoding and encoding JSON
解码和编码 JSON

使用 jsonDecode() 将 JSON 编码的字符串解码为 Dart 对象:

// NOTE: Be sure to use double quotes ("),
// not single quotes ('), inside the JSON string.
// This string is JSON, not Dart.
var jsonString = '''
  [
    {"score": 40},
    {"score": 80}
  ]
''';

var scores = jsonDecode(jsonString);
assert(scores is List);

var firstScore = scores[0];
assert(firstScore is Map);
assert(firstScore['score'] == 40);

将支持的 Dart 对象编码为 JSON 格式的字符串,并使用 jsonEncode() 中:

var scores = [
  {'score': 40},
  {'score': 80},
  {'score': 100, 'overtime': true, 'special_guest': null},
];

var jsonText = jsonEncode(scores);
assert(
  jsonText ==
      '[{"score":40},{"score":80},'
          '{"score":100,"overtime":true,'
          '"special_guest":null}]',
);

只有 int、double、String、bool、null、List 或 Map(带有字符串键)类型的对象才能直接编码为 JSON。List 和 Map 对象以递归方式编码。

对于不可直接编码的对象,您有两个选项。第一种是使用第二个参数调用 jsonEncode() :一个返回可直接编码的对象的函数。第二个选项是省略第二个参数,在这种情况下,编码器会调用对象的 toJson() 方法。

有关 JSON 相关包的更多示例和链接,请参阅 使用 JSON。

Decoding and encoding UTF-8 characters
解码和编码 UTF-8 字符

使用 utf8.decode() 将 UTF8 编码的字节解码为 Dart 字符串:

List<int> utf8Bytes = [
  0xc3, 0x8e, 0xc3, 0xb1, 0xc5, 0xa3, 0xc3, 0xa9,
  0x72, 0xc3, 0xb1, 0xc3, 0xa5, 0xc5, 0xa3, 0xc3,
  0xae, 0xc3, 0xb6, 0xc3, 0xb1, 0xc3, 0xa5, 0xc4,
  0xbc, 0xc3, 0xae, 0xc5, 0xbe, 0xc3, 0xa5, 0xc5,
  0xa3, 0xc3, 0xae, 0xe1, 0xbb, 0x9d, 0xc3, 0xb1,
];

var funnyWord = utf8.decode(utf8Bytes);

assert(funnyWord == 'Îñţérñåţîöñåļîžåţîờñ');

要将 UTF-8 字符流转换为 Dart 字符串,请指定 utf8.decoder 添加到 Stream transform() 方法中:

var lines = utf8.decoder.bind(inputStream).transform(const LineSplitter());
try {
  await for (final line in lines) {
    print('Got ${line.length} characters from stream');
  }
  print('file is now closed');
} catch (e) {
  print(e);
}

使用 utf8.encode() 将 Dart 字符串编码为 UTF8 编码的字节列表:

Uint8List encoded = utf8.encode('Îñţérñåţîöñåļîžåţîờñ');

assert(encoded.length == utf8Bytes.length);
for (int i = 0; i < encoded.length; i++) {
  assert(encoded[i] == utf8Bytes[i]);
}

Other functionality  其他功能

 dart:convert 库也有 ASCII 和 ISO-8859-1 (Latin1) 的转换器。有关详细信息,请参阅 dart:convert 库的 API 参考

dart:io

dart:io 库提供了 API 来处理文件、目录、进程、套接字、WebSockets 以及 HTTP 客户端和服务器。

反馈重点提醒

只有非 Web Flutter 应用、 命令行脚本和服务器可以导入和使用 dart:io,但不能导入和使用 Web 应用。

一般来说,dart:io 库实现并推广异步 API。同步方法很容易阻止应用程序,使其难以扩展。因此,大多数作通过 Future 或 Stream 对象返回结果,这是 Node.js 等现代服务器平台的常见模式。

dart:io 库中的少数同步方法在方法名称上清楚地标有 Sync 后缀。

要使用 dart:io 库,您必须导入它:

import 'dart:io';

Files and directories  文件和目录

#

I/O 库使命令行应用能够读取和写入文件,并且 浏览目录。您有两种选择来读取 file: all at once,或 streaming。一次读取所有文件需要 足够的内存来存储文件的所有内容。如果文件非常 large 或者你想在读取它时处理它,你应该使用 Stream 中,如流式处理文件内容 

Reading a file as text
将文件作为文本读取

#

读取使用 UTF-8 编码的文本文件时,您可以使用 readAsString() 读取整个文件内容。当各个行很重要时,您可以使用 readAsLines() 在这两种情况下,都会返回一个 Future 对象,该对象将文件的内容作为一个或多个字符串提供。

void main() async {
  var config = File('config.txt');

  // Put the whole file in a single string.
  var stringContents = await config.readAsString();
  print('The file is ${stringContents.length} characters long.');

  // Put each line of the file into its own string.
  var lines = await config.readAsLines();
  print('The file is ${lines.length} lines long.');
}

Reading a file as binary
将文件读取为二进制文件

#

以下代码将整个文件作为字节读取到 int 列表中。对 readAsBytes() 的调用返回一个 Future,它会在结果可用时提供结果。

void main() async {
  var config = File('config.txt');

  var contents = await config.readAsBytes();
  print('The file is ${contents.length} bytes long.');
}

Handling errors  处理错误

#

要捕获错误,使其不会导致未捕获的异常,你可以在 Future 上注册一个 catchError 处理程序,或者(在异步函数中)使用 try-catch:

void main() async {
  var config = File('config.txt');
  try {
    var contents = await config.readAsString();
    print(contents);
  } catch (e) {
    print(e);
  }
}

Streaming file contents  流式处理文件内容

#

 使用 Stream 读取文件,一次读取一点。您可以使用 Stream API 或 await for,是 Dart 的一部分 asynchrony 支持。

import 'dart:io';
import 'dart:convert';

void main() async {
  var config = File('config.txt');
  Stream<List<int>> inputStream = config.openRead();

  var lines = utf8.decoder.bind(inputStream).transform(const LineSplitter());
  try {
    await for (final line in lines) {
      print('Got ${line.length} characters from stream');
    }
    print('file is now closed');
  } catch (e) {
    print(e);
  }
}

Writing file contents  写入文件内容

#

您可以使用 IOSink 将数据写入文件。使用 File openWrite() 方法获取可写入的 IOSink。默认模式 FileMode.write 完全覆盖文件中的现有数据。

var logFile = File('log.txt');
var sink = logFile.openWrite();
sink.write('FILE ACCESSED ${DateTime.now()}\n');
await sink.flush();
await sink.close();

  • flush() 强制将缓冲区中的数据写入磁盘。
  • 使用 await 确保刷新操作完成后再继续执行后续代码。
  • 注意:虽然 flush() 是可选的(因为 close() 也会自动刷新),但显式调用可以确保数据及时写入。
  • close() 关闭 IOSink,释放资源。
  • 必须调用 close(),否则文件可能不会被正确保存。
  • 使用 await 确保关闭操作完成。

要添加到文件末尾,请使用可选的 mode 参数指定 FileMode.append

var sink = logFile.openWrite(mode: FileMode.append);

  • 默认以追加模式打开文件(不会覆盖原有内容)。
  • 如果要以覆盖模式打开,可以传递参数:

var sink = logFile.openWrite(mode: FileMode.write);

要写入二进制数据,请使用 add(List<int> data)。

Listing files in a directory
列出目录中的文件

#

查找目录的所有文件和子目录是一个异步作。list() 方法返回一个 Stream,当遇到文件或目录时,该 Stream 会发出一个对象。

void main() async {
  var dir = Directory('tmp');

  try {
    var dirList = dir.list();
    await for (final FileSystemEntity f in dirList) {
      if (f is File) {
        print('Found file ${f.path}');
      } else if (f is Directory) {
        print('Found dir ${f.path}');
      }
    }
  } catch (e) {
    print(e.toString());
  }
}

Other common functionality
其他常见功能

File 和 Directory 类包含其他功能,包括但不限于:

  • Creating a file or directory: create() in File and Directory
    创建文件或目录:File 和 Directory 中的 create()
  • Deleting a file or directory: delete() in File and Directory
    删除文件或目录:文件和目录中的 delete()
  • Getting the length of a file: length() in File
    获取文件的长度:File 中的 length()
  • Getting random access to a file: open() in File
    获取对文件的随机访问权限:File 中的 open()

有关方法的完整列表,请参阅 File 和 Directory 的 API 文档。

HTTP clients and servers
HTTP 客户端和服务器

#


dart:io 库提供了命令行应用程序可用于访问 HTTP 资源以及运行 HTTP 服务器的类。

HTTP server  HTTP 服务器


HttpServer 类提供了用于构建 Web 服务器的低级功能。您可以匹配请求处理程序、设置标头、流数据等。

以下示例 Web 服务器返回简单文本信息。该服务器监听端口 8888 并寻址 127.0.0.1 (localhost),响应路径 /dart 的请求。对于任何其他路径,响应为状态代码 404 (page not found)。

void main() async {
  final requests = await HttpServer.bind('localhost', 8888);
  await for (final request in requests) {
    processRequest(request);
  }
}

void processRequest(HttpRequest request) {
  print('Got request for ${request.uri.path}');
  final response = request.response;
  if (request.uri.path == '/dart') {
    response
      ..headers.contentType = ContentType('text', 'plain')
      ..write('Hello from the server');
  } else {
    response.statusCode = HttpStatus.notFound;
  }
  response.close();
}

HTTP client  HTTP 客户端

#

您应该避免直接使用 dart:io 发出 HTTP 请求。dart:io 中的 HttpClient 类是平台相关的 并绑定到单个实现。 相反,请使用更高级别的库,例如 package:http 的

从 Internet 获取数据教程介绍了如何使用 package:http 发出 HTTP 请求。

More information  更多信息


本页展示了如何使用 dart:io 库的主要功能。除了本节讨论的 API 之外,dart:io 库还提供了用于 processes、sockets 和Web 套接字。 有关服务器端和命令行应用程序开发的更多信息,请参阅 服务器端 Dart 概述。

JavaScript 互作性

Dart Web 平台支持使用 dart:js_interop 与 JavaScript 应用和库以及浏览器 API 进行通信。

Web 开发人员可以从在 Dart 代码中使用外部 JS 库中受益,而无需在 Dart 中重写任何内容。

Next-generation JS interop
下一代 JS 互作

Dart 团队最近对功能和 API 的集合进行了全面改革,允许开发人员在其 Dart 代码中访问 JavaScript 和浏览器绑定。下一代 Web 互作不仅改善了用户体验,还支持 Wasm 支持,使 Dart 与 Web 的未来保持一致。

下表将 Dart 的新 JS 和 Web 互作解决方案与过去的对应解决方案进行了映射:

New interop libraries  新的互作库 Previous libraries  以前的库
package:web  包:Web dart:html  dart:html
dart:indexed_db  飞镖:indexed_dbdartdart:indexed_db  飞镖:indexed_db
dart:svg  飞镖:SVGdartdart:svg  飞镖:SVG
dart:web_audio  飞镖:web_audiodartdart:web_audio  飞镖:web_audio
dart:web_gl  飞镖:web_gldartdart:web_gl  飞镖:web_gl
dart:js_interop  飞镖:js_interopdartdart:js_interop  飞镖:js_interop
dart:js_interop_unsafe  飞镖:js_interop_unsafedartdart:js_interop_unsafe  飞镖:js_interop_unsafe
package:js  包:js
dart:js  飞镖:jsdartdart:js  飞镖:js
dart:js_util  飞镖:js_utildartdart:js_util  飞镖:js_util

Dart 互作故事已经进行了大量开发一段时间了;查看过去的 JS 互作页面,了解有关过去迭代的更深入摘要。

Overview

#

有关如何编写和使用 JavaScript 互作的信息:

有关与 Web API 交互的信息:

有关教程和帮助:

有关 JavaScript 互作的其他文档:


网站公告

今日签到

点亮在社区的每一天
去签到