@@ -150,6 +150,113 @@ class Isolate {
150
150
/// inspect the isolate and see uncaught errors or when it terminates.
151
151
Isolate (this .controlPort, {this .pauseCapability, this .terminateCapability});
152
152
153
+ /// Runs [computation] in a new isolate and returns the result.
154
+ ///
155
+ /// ```dart
156
+ /// int slowFib(int n) =>
157
+ /// n <= 1 ? 1 : slowFib(n - 1) + slowFib(n - 2);
158
+ ///
159
+ /// // Compute without blocking current isolate.
160
+ /// var fib40 = await Isolate.run(() => slowFib(40));
161
+ /// ```
162
+ ///
163
+ /// If [computation] is asynchronous (returns a `Future<R>` ) then
164
+ /// that future is awaited in the new isolate, completing the entire
165
+ /// asynchronous computation, before returning the result.
166
+ ///
167
+ /// ```dart
168
+ /// int slowFib(int n) =>
169
+ /// n <= 1 ? 1 : slowFib(n - 1) + slowFib(n - 2);
170
+ /// Stream<int> fibStream() async* {
171
+ /// for (var i = 0;; i++) yield slowFib(i);
172
+ /// }
173
+ ///
174
+ /// // Returns `Future<int>`.
175
+ /// var fib40 = await Isolate.run(() => fibStream().elementAt(40));
176
+ /// ```
177
+ ///
178
+ /// If [computation] throws, the isolate is terminated and this
179
+ /// function throws the same error.
180
+ ///
181
+ /// ```dart import:convert
182
+ /// Future<int> eventualError() async {
183
+ /// await Future.delayed(const Duration(seconds: 1));
184
+ /// throw StateError("In a bad state!");
185
+ /// }
186
+ ///
187
+ /// try {
188
+ /// await Isolate.run(eventualError);
189
+ /// } on StateError catch (e, s) {
190
+ /// print(e.message); // In a bad state!
191
+ /// print(LineSplitter.split("$s").first); // Contains "eventualError"
192
+ /// }
193
+ /// ```
194
+ /// Any uncaught asynchronous errors will terminate the computation as well,
195
+ /// but will be reported as a [RemoteError] because [addErrorListener]
196
+ /// does not provide the original error object.
197
+ ///
198
+ /// The result is sent using [exit] , which means it's sent to this
199
+ /// isolate without copying.
200
+ ///
201
+ /// The [computation] function and its result (or error) must be
202
+ /// sendable between isolates.
203
+ ///
204
+ /// The [debugName] is only used to name the new isolate for debugging.
205
+ @Since ("2.19" )
206
+ static Future <R > run <R >(FutureOr <R > computation (), {String ? debugName}) {
207
+ var result = Completer <R >();
208
+ var resultPort = RawReceivePort ();
209
+ resultPort.handler = (response) {
210
+ resultPort.close ();
211
+ if (response == null ) {
212
+ // onExit handler message, isolate terminated without sending result.
213
+ result.completeError (
214
+ RemoteError ("Computation ended without result" , "" ),
215
+ StackTrace .empty);
216
+ return ;
217
+ }
218
+ var list = response as List <Object ?>;
219
+ if (list.length == 2 ) {
220
+ var remoteError = list[0 ];
221
+ var remoteStack = list[1 ];
222
+ if (remoteStack is StackTrace ) {
223
+ // Typed error.
224
+ result.completeError (remoteError! , remoteStack);
225
+ } else {
226
+ // onError handler message, uncaught async error.
227
+ // Both values are strings, so calling `toString` is efficient.
228
+ var error =
229
+ RemoteError (remoteError.toString (), remoteStack.toString ());
230
+ result.completeError (error, error.stackTrace);
231
+ }
232
+ } else {
233
+ assert (list.length == 1 );
234
+ result.complete (list[0 ] as R );
235
+ }
236
+ };
237
+ try {
238
+ Isolate .spawn (_RemoteRunner ._remoteExecute,
239
+ _RemoteRunner <R >(computation, resultPort.sendPort),
240
+ onError: resultPort.sendPort,
241
+ onExit: resultPort.sendPort,
242
+ errorsAreFatal: true ,
243
+ debugName: debugName)
244
+ .then <void >((_) {}, onError: (error, stack) {
245
+ // Sending the computation failed asynchronously.
246
+ // Do not expect a response, report the error asynchronously.
247
+ resultPort.close ();
248
+ result.completeError (error, stack);
249
+ });
250
+ } on Object {
251
+ // Sending the computation failed synchronously.
252
+ // This is not expected to happen, but if it does,
253
+ // the synchronous error is respected and rethrown synchronously.
254
+ resultPort.close ();
255
+ rethrow ;
256
+ }
257
+ return result.future;
258
+ }
259
+
153
260
/// An [Isolate] object representing the current isolate.
154
261
///
155
262
/// The current isolate for code using [current]
@@ -807,3 +914,62 @@ abstract class TransferableTypedData {
807
914
/// transferable bytes, even if the calls occur in different isolates.
808
915
ByteBuffer materialize ();
809
916
}
917
+
918
+ /// Parameter object used by [Isolate.run] .
919
+ ///
920
+ /// The [_remoteExecute] function is run in a new isolate with a
921
+ /// [_RemoteRunner] object as argument.
922
+ class _RemoteRunner <R > {
923
+ /// User computation to run.
924
+ final FutureOr <R > Function () computation;
925
+
926
+ /// Port to send isolate computation result on.
927
+ ///
928
+ /// Only one object is ever sent on this port.
929
+ /// If the value is `null` , it is sent by the isolate's "on-exit" handler
930
+ /// when the isolate terminates without otherwise sending value.
931
+ /// If the value is a list with one element,
932
+ /// then it is the result value of the computation.
933
+ /// Otherwise it is a list with two elements representing an error.
934
+ /// If the error is sent by the isolate's "on-error" uncaught error handler,
935
+ /// then the list contains two strings. This also terminates the isolate.
936
+ /// If sent manually by this class, after capturing the error,
937
+ /// the list contains one non-`null` [Object] and one [StackTrace] .
938
+ final SendPort resultPort;
939
+
940
+ _RemoteRunner (this .computation, this .resultPort);
941
+
942
+ /// Run in a new isolate to get the result of [computation] .
943
+ ///
944
+ /// The result is sent back on [resultPort] as a single-element list.
945
+ /// A two-element list sent on the same port is an error result.
946
+ /// When sent by this function, it's always an object and a [StackTrace] .
947
+ /// (The same port listens on uncaught errors from the isolate, which
948
+ /// sends two-element lists containing [String] s instead).
949
+ static void _remoteExecute (_RemoteRunner <Object ?> runner) {
950
+ runner._run ();
951
+ }
952
+
953
+ void _run () async {
954
+ R result;
955
+ try {
956
+ var potentiallyAsyncResult = computation ();
957
+ if (potentiallyAsyncResult is Future <R >) {
958
+ result = await potentiallyAsyncResult;
959
+ } else {
960
+ result = potentiallyAsyncResult;
961
+ }
962
+ } catch (e, s) {
963
+ // If sending fails, the error becomes an uncaught error.
964
+ Isolate .exit (resultPort, _list2 (e, s));
965
+ }
966
+ Isolate .exit (resultPort, _list1 (result));
967
+ }
968
+
969
+ /// Helper function to create a one-element non-growable list.
970
+ static List <Object ?> _list1 (Object ? value) => List .filled (1 , value);
971
+
972
+ /// Helper function to create a two-element non-growable list.
973
+ static List <Object ?> _list2 (Object ? value1, Object ? value2) =>
974
+ List .filled (2 , value1)..[1 ] = value2;
975
+ }
0 commit comments