如何使用 package:js 在 Dart 中映射返回异步迭代器的 JavaScript 函数?

问题描述 投票:0回答:2

我想使用 Dart/Flutter 的

window.showDirectoryPicker()
方法。从 JavaScript 使用它来允许用户选择一个目录,然后列出它的内容,如下所示:

const dirHandle = await window.showDirectoryPicker();
for await (const entry of dirHandle.values()) {
  console.log(entry.name);
}

FileSystemDirectoryHandle.values()
的文档说它返回“一个新的数组迭代器,其中包含
FileSystemDirectoryHandle
对象中每个索引的值”。它没有具体说明,尽管它似乎是一个 async 迭代器,因为枚举它需要
await for

我正在尝试创建绑定以从 Dart 调用它。我希望我的绑定应该返回

Stream<FileSystemHandle>
,尽管我找不到任何有关执行此操作的示例或文档。我尝试了很多组合,但这是我现在所拥有的:

@JS()
library js_lib;

import 'dart:html';

import 'package:js/js.dart';
import 'package:js/js_util.dart';

Future<FileSystemDirectoryHandle> showDirectoryPicker() =>
    promiseToFuture(callMethod(window, 'showDirectoryPicker', []));

@JS()
abstract class FileSystemDirectoryHandle extends FileSystemHandle {
  external Stream<FileSystemHandle> values();
}

@JS()
abstract class FileSystemHandle {
  String get name;
}

@JS()
abstract class FileSystemFileHandle extends FileSystemHandle {}

我想这样称呼它:

final dirHandle = await showDirectoryPicker();
await for (final item in dirHandle.values()) {
  print(item.name);
}

但是这会失败,如错误所示:

错误:需要“Stream”类型的值,但得到“NativeJavaScriptObject”类型之一

我怀疑我可能需要一些东西来将 JS 迭代器转换为 Dart 流(类似于我的

promiseToFuture
调用),尽管我找不到方法来做到这一点(我也无法向
 添加方法) FileSystemDirectoryHandle
不是
external

javascript flutter dart webapi dart-js-interop
2个回答
3
投票

编辑:这似乎适用于

build_runner serve
,但不适用于
build_runner serve --release
。我已提交问题以确定这是否是一个错误或者这是错误的方法。


意识到 JS 中的异步迭代器只是

next()
返回承诺的迭代器之后,我能够编写一个小函数,只需手动调用该方法并在
async*
函数中产生:

extension FileSystemDirectoryHandleExtensions on FileSystemDirectoryHandle {
  Stream<FileSystemHandle> values() =>
      _asyncIterator<FileSystemHandle>(callMethod(this, 'values', []));
}

Stream<T> _asyncIterator<T>(jsIterator) async* {
  while (true) {
    final next = await promiseToFuture(callMethod(jsIterator, 'next', []));
    if (getProperty(next, 'done')) {
      break;
    }
    yield getProperty(next, 'value');
  }
}

这是使用上面的原始 Dart 代码调用的,并根据需要打印文件名。


0
投票

你可以这样做:

import 'dart:js_interop';
import 'package:web/web.dart';

extension type FileSystemDirectoryHandleExt._(FileSystemDirectoryHandle _)
    implements FileSystemHandle, JSObject {
  external JsAsyncIterator<JSAny> keys();
}

extension type JsAsyncIterator<T extends JSAny>._(JSObject _)
    implements JSObject {
  external JSPromise<JsAsyncIteratorState<T>> next();

  Stream<T> asStream() async* {
    while (true) {
      final result = await next().toDart;
      if (result.done) break;
      yield result.value;
    }
  }
}

extension type JsAsyncIteratorState<T extends JSAny>._(JSObject _)
    implements JSObject {
  external bool get done;

  external T get value;
}

然后像这样使用它:

final storageRoot = await window.navigator.storage.getDirectory().toDart;
    
final storageRootExt = storageRoot as FileSystemDirectoryHandleExt;
    
await for (final JSString key in storageRootExt.keys().asStream()) {
  // ...
}

values()
entries()
可以按照与上面
keys()
类似的方式添加。请参阅MDN

我不确定为什么这还没有在 package:web 中。难道是我看的不够仔细?

© www.soinside.com 2019 - 2024. All rights reserved.