是否可以在单个异步函数中封装重复的发送/响应到同一个dart隔离区?
背景:
为了设计一个方便的API,我希望有一个函数异步返回隔离生成的结果,例如
var ans = await askIsolate(isolateArgs);
如果我直接使用spawnUri调用生成的响应,这可以正常工作,例如
Future<String> askIsolate(Map<String,dynamic> isolateArgs) {
ReceivePort response = new ReceivePort();
var uri = Uri.parse(ISOLATE_URI);
Future<Isolate> remote = Isolate.spawnUri(uri, [JSON.encode(isolateArgs)], response.sendPort);
return remote.then((i) => response.first)
.catchError((e) { print("Failed to spawn isolate"); })
.then((msg) => msg.toString());
}
然而,上述方法的缺点是,如果我需要重复调用askIsolate,则每次都必须生成隔离。
我希望与正在运行的隔离区进行通信,这当然可以通过让隔离区将sendPort返回给调用方来实现。但我相信自2013 Isolate refactoring以来,这需要调用者监听receivePort上的后续消息,从而无法在单个异步函数中进行封装。
是否有某种机制可以实现这一目标,而我却错过了?
答案 0 :(得分:2)
答案取决于您打算如何使用分离
您打算让它无限期地运行,发送输入并期望异步接收答案吗?
你想立即发送隔离多个(但是有限的)输入,期望异步接收答案,然后关闭隔离?
我猜测后者,你的askIsolate()
函数需要立即返回Future
,而不是在收到所有答案时完成。
await for
循环可用于侦听流并消耗事件直到它关闭。
我不熟悉隔离物,所以我希望这没关系,我还没有测试过它。我假设隔离终止并且响应关闭。
String askIsolate(Map<String,dynamic> isolateArgs) async {
ReceivePort response = new ReceivePort();
var uri = Uri.parse(ISOLATE_URI);
Isolate.spawnUri(uri, [JSON.encode(isolateArgs)], response.sendPort)
.catchError((e)) {
throw ...;
});
List<String> answers = new List<String>;
await for(var answer in response) {
out.add(answer.toString());
}
return answers;
}
注意:
response
是您正在收听答案的信息流。它是在产生隔离之前创建的所以你不需要(也可能不应该)在听之前等待隔离未来完成。
我做了askIsolate()
异步,因为这样可以很容易地立即返回一个在函数返回时完成的未来 - 没有用.then(...)
链条进行的那些繁琐的讨论,我个人觉得令人困惑,难以阅读。
then(...).catchError(...)
样式代码会更好地写成:
Isolate.spawnUri(uri, [JSON.encode(isolateArgs)], response.sendPort)
.catchError((e) { ... });
return response.first)
.then((msg) => msg.toString());
我相信在创建隔离专区之后延迟将一个catchError处理程序附加到该行可能允许将来在处理程序到位之前完成错误。
答案 1 :(得分:2)
我还建议在IsolateRunner
中查看package:isolate
,它旨在解决这样的问题 - 在同一个隔离中调用一个函数几次,而不是在创建隔离时调用一次。
如果您不想要,那么还有其他更原始的选项
异步函数可以等待期货或流,而ReceivePort
是一个流。
对于快速入侵,您可以在响应流上使用await for
执行某些操作,但它不会非常方便。
将ReceivePort
中的StreamQueue
包裹在package:async
中是更好的选择。这允许您将个别事件转换为期货。类似的东西:
myFunc() async {
var responses = new ReceivePort();
var queue = new StreamQueue(responses);
// queryFunction sends its own SendPort on the port you pass to it.
var isolate = await isolate.spawn(queryFunction, [], responses.sendPort);
var queryPort = await queue.next();
for (var something in somethingToDo) {
queryPort.send(something);
var response = await queue.next();
doSomethingWithIt(response);
}
queryPort.send("shutdown command");
// or isolate.kill(), but it's better to shut down cleanly.
responses.close(); // Don't forget to close the receive port.
}
答案 2 :(得分:1)
基于以上lrn评论的快速工作示例如下。该示例通过spawnURI初始化隔离,然后通过传递一个新的ReceivePort与隔离进行通信,在该ReceivePort上需要回复。这允许askIsolate直接从正在运行的spawnURI隔离区返回响应。
为清楚起见,省略了错误处理。
隔离代码:
import 'dart:isolate';
import 'dart:convert' show JSON;
main(List<String> initArgs, SendPort replyTo) async {
ReceivePort receivePort = new ReceivePort();
replyTo.send(receivePort.sendPort);
receivePort.listen((List<dynamic> callArgs) async {
SendPort thisResponsePort = callArgs.removeLast(); //last arg must be the offered sendport
thisResponsePort.send("Map values: " + JSON.decode(callArgs[0]).values.join(","));
});
}
致电代码:
import 'dart:async';
import 'dart:isolate';
import 'dart:convert';
const String ISOLATE_URI = "http://localhost/isolates/test_iso.dart";
SendPort isolateSendPort = null;
Future<SendPort> initIsolate(Uri uri) async {
ReceivePort response = new ReceivePort();
await Isolate.spawnUri(uri, [], response.sendPort, errorsAreFatal: true);
print("Isolate spawned from $ISOLATE_URI");
return await response.first;
}
Future<dynamic> askIsolate(Map<String,String> args) async {
if (isolateSendPort == null) {
print("ERROR: Isolate has not yet been spawned");
isolateSendPort = await initIsolate(Uri.parse(ISOLATE_URI)); //try again
}
//Send args to the isolate, along with a receiveport upon which we listen for first response
ReceivePort response = new ReceivePort();
isolateSendPort.send([JSON.encode(args), response.sendPort]);
return await response.first;
}
main() async {
isolateSendPort = await initIsolate(Uri.parse(ISOLATE_URI));
askIsolate({ 'foo':'bar', 'biz':'baz'}).then(print);
askIsolate({ 'zab':'zib', 'rab':'oof'}).then(print);
askIsolate({ 'One':'Thanks', 'Two':'lrn'}).then(print);
}
<强>输出强>
Isolate spawned from http://localhost/isolates/test_iso.dart
Map values: bar,baz
Map values: zib,oof
Map values: Thanks,lrn