@@ -144,6 +144,106 @@ class Isolate {
144
144
/// inspect the isolate and see uncaught errors or when it terminates.
145
145
Isolate (this .controlPort, {this .pauseCapability, this .terminateCapability});
146
146
147
+ /// Runs [computation] in a new isolate and returns the result.
148
+ ///
149
+ /// ```dart
150
+ /// int slowFib(int n) =>
151
+ /// n <= 1 ? 1 : slowFib(n - 1) + slowFib(n - 2);
152
+ ///
153
+ /// // Compute without blocking current isolate.
154
+ /// var fib40 = await Isolate.run(() => slowFib(40));
155
+ /// ```
156
+ ///
157
+ /// If [computation] is asynchronous (returns a `Future<R>` ) then
158
+ /// that future is awaited in the new isolate, completing the entire
159
+ /// asynchronous computation, before returning the result.
160
+ ///
161
+ /// ```dart
162
+ /// int slowFib(int n) =>
163
+ /// n <= 1 ? 1 : slowFib(n - 1) + slowFib(n - 2);
164
+ /// Stream<int> fibStream() async* {
165
+ /// for (var i = 0;; i++) yield slowFib(i);
166
+ /// }
167
+ ///
168
+ /// // Returns `Future<int>`.
169
+ /// var fib40 = await Isolate.run(() => fibStream().elementAt(40));
170
+ /// ```
171
+ ///
172
+ /// If [computation] throws, the isolate is terminated and this
173
+ /// function throws the same error.
174
+ ///
175
+ /// ```dart import:convert
176
+ /// Future<int> eventualError() async {
177
+ /// await Future.delayed(const Duration(seconds: 1));
178
+ /// throw StateError("In a bad state!");
179
+ /// }
180
+ ///
181
+ /// try {
182
+ /// await Isolate.run(eventualError);
183
+ /// } on StateError catch (e, s) {
184
+ /// print(e.message); // In a bad state!
185
+ /// print(LineSplitter.split("$s").first); // Contains "eventualError"
186
+ /// }
187
+ /// ```
188
+ /// Any uncaught asynchronous errors will terminate the computation as well,
189
+ /// but will be reported as a [RemoteError] because [addErrorListener]
190
+ /// does not provide the original error object.
191
+ ///
192
+ /// The result is sent using [exit] , which means it's sent to this
193
+ /// isolate without copying.
194
+ ///
195
+ /// The [computation] function and its result (or error) must be
196
+ /// sendable between isolates.
197
+ ///
198
+ /// The [debugName] is only used to name the new isolate for debugging.
199
+ @Since ("2.17" )
200
+ static Future <R > run <R >(FutureOr <R > computation (), {String ? debugName}) {
201
+ var result = Completer <R >();
202
+ var resultPort = RawReceivePort ();
203
+ resultPort.handler = (response) {
204
+ resultPort.close ();
205
+ if (response == null ) {
206
+ // onExit handler message, isolate terminated without sending result.
207
+ result.completeError (
208
+ RemoteError ("Computation ended without result" , "" ),
209
+ StackTrace .empty);
210
+ return ;
211
+ }
212
+ var list = response as List <Object ?>;
213
+ if (list.length == 2 ) {
214
+ var remoteError = list[0 ];
215
+ var remoteStack = list[1 ];
216
+ if (remoteStack is StackTrace ) {
217
+ // Typed error.
218
+ result.completeError (remoteError! , remoteStack);
219
+ } else {
220
+ // onError handler message, uncaught async error.
221
+ // Both values are strings, so calling `toString` is efficient.
222
+ var error =
223
+ RemoteError (remoteError.toString (), remoteStack.toString ());
224
+ result.completeError (error, error.stackTrace);
225
+ }
226
+ } else {
227
+ assert (list.length == 1 );
228
+ result.complete (list[0 ] as R );
229
+ }
230
+ };
231
+ try {
232
+ Isolate .spawn (_RemoteRunner ._remoteExecute,
233
+ _RemoteRunner <R >(computation, resultPort.sendPort),
234
+ onError: resultPort.sendPort,
235
+ onExit: resultPort.sendPort,
236
+ errorsAreFatal: true ,
237
+ debugName: debugName)
238
+ .then <void >((_) {}, onError: result.completeError);
239
+ } on Object {
240
+ // Sending the computation failed.
241
+ resultPort.close ();
242
+ rethrow ;
243
+ }
244
+ return result.future;
245
+ }
246
+
147
247
/// An [Isolate] object representing the current isolate.
148
248
///
149
249
/// The current isolate for code using [current]
@@ -345,7 +445,7 @@ class Isolate {
345
445
/// of the isolate identified by [controlPort] ,
346
446
/// the pause request is ignored by the receiving isolate.
347
447
Capability pause ([Capability ? resumeCapability]) {
348
- resumeCapability ?? = new Capability ();
448
+ resumeCapability ?? = Capability ();
349
449
_pause (resumeCapability);
350
450
return resumeCapability;
351
451
}
@@ -533,12 +633,12 @@ class Isolate {
533
633
var listMessage = message as List <Object ?>;
534
634
var errorDescription = listMessage[0 ] as String ;
535
635
var stackDescription = listMessage[1 ] as String ;
536
- var error = new RemoteError (errorDescription, stackDescription);
636
+ var error = RemoteError (errorDescription, stackDescription);
537
637
controller.addError (error, error.stackTrace);
538
638
}
539
639
540
640
controller.onListen = () {
541
- RawReceivePort receivePort = new RawReceivePort (handleError);
641
+ RawReceivePort receivePort = RawReceivePort (handleError);
542
642
port = receivePort;
543
643
this .addErrorListener (receivePort.sendPort);
544
644
};
@@ -765,7 +865,7 @@ class RemoteError implements Error {
765
865
final StackTrace stackTrace;
766
866
RemoteError (String description, String stackDescription)
767
867
: _description = description,
768
- stackTrace = new StackTrace .fromString (stackDescription);
868
+ stackTrace = StackTrace .fromString (stackDescription);
769
869
String toString () => _description;
770
870
}
771
871
@@ -795,3 +895,62 @@ abstract class TransferableTypedData {
795
895
/// transferable bytes, even if the calls occur in different isolates.
796
896
ByteBuffer materialize ();
797
897
}
898
+
899
+ /// Parameter object used by [Isolate.run] .
900
+ ///
901
+ /// The [_remoteExecute] function is run in a new isolate with a
902
+ /// [_RemoteRunner] object as argument.
903
+ class _RemoteRunner <R > {
904
+ /// User computation to run.
905
+ final FutureOr <R > Function () computation;
906
+
907
+ /// Port to send isolate computation result on.
908
+ ///
909
+ /// Only one object is ever sent on this port.
910
+ /// If the value is `null` , it is sent by the isolate's "on-exit" handler
911
+ /// when the isolate terminates without otherwise sending value.
912
+ /// If the value is a list with one element,
913
+ /// then it is the result value of the computation.
914
+ /// Otherwise it is a list with two elements representing an error.
915
+ /// If the error is sent by the isolate's "on-error" uncaught error handler,
916
+ /// then the list contains two strings. This also terminates the isolate.
917
+ /// If sent manually by this class, after capturing the error,
918
+ /// the list contains one non-`null` [Object] and one [StackTrace] .
919
+ final SendPort resultPort;
920
+
921
+ _RemoteRunner (this .computation, this .resultPort);
922
+
923
+ /// Run in a new isolate to get the result of [computation] .
924
+ ///
925
+ /// The result is sent back on [resultPort] as a single-element list.
926
+ /// A two-element list sent on the same port is an error result.
927
+ /// When sent by this function, it's always an object and a [StackTrace] .
928
+ /// (The same port listens on uncaught errors from the isolate, which
929
+ /// sends two-element lists containing [String] s instead).
930
+ static void _remoteExecute (_RemoteRunner <Object ?> runner) {
931
+ runner._run ();
932
+ }
933
+
934
+ void _run () async {
935
+ R result;
936
+ try {
937
+ var potentiallyAsyncResult = computation ();
938
+ if (potentiallyAsyncResult is Future <R >) {
939
+ result = await potentiallyAsyncResult;
940
+ } else {
941
+ result = potentiallyAsyncResult as R ;
942
+ }
943
+ } catch (e, s) {
944
+ // If sending fails, the error becomes an uncaught error.
945
+ Isolate .exit (resultPort, _list2 (e, s));
946
+ }
947
+ Isolate .exit (resultPort, _list1 (result));
948
+ }
949
+
950
+ /// Helper function to create a one-element non-growable list.
951
+ static List <Object ?> _list1 (Object ? value) => List .filled (1 , value);
952
+
953
+ /// Helper function to create a two-element non-growable list.
954
+ static List <Object ?> _list2 (Object ? value1, Object ? value2) =>
955
+ List .filled (2 , value1)..[1 ] = value2;
956
+ }
0 commit comments