asyncEval method
Future<InstanceRef?>
asyncEval(
- String expression, {
- required Disposable? isAlive,
- Map<
String, String> ? scope,
A safeEval variant that can use await
.
This is useful to obtain the value emitted by a future, by potentially doing:
final result = await asyncEval('await Future.value(42)');
where result
will be an InstanceRef
that points to 42
.
If the FutureOr awaited threw, asyncEval will throw a FutureFailedException, which can be caught to access the StackTrace and error.
Implementation
Future<InstanceRef?> asyncEval(
String expression, {
required Disposable? isAlive,
Map<String, String>? scope,
}) async {
final futureId = _nextAsyncEvalId++;
// start awaiting the event before starting the evaluation, in case the
// event is received before the eval function completes.
final future = serviceManager.service!.onExtensionEvent.firstWhere((event) {
return event.extensionKind == 'future_completed' &&
event.extensionData!.data['future_id'] == futureId &&
// Using `_clientId` here as if two chrome tabs open the devtool, it is
// possible to have conflicts on `future_id`
event.extensionData!.data['client_id'] == _clientId;
});
final readerGroup = 'asyncEval-$futureId';
/// Workaround to not being able to import libraries directly from an evaluation
final postEventRef = await _dartDeveloperEval.safeEval(
'postEvent',
isAlive: isAlive,
);
final widgetInspectorServiceRef = await _widgetInspectorEval.safeEval(
'WidgetInspectorService.instance',
isAlive: isAlive,
);
final readerId = await safeEval(
// since we are awaiting the Future, we need to make sure that during the awaiting,
// the "reader" is not GCed
'widgetInspectorService.toId(<dynamic>[], "$readerGroup")',
isAlive: isAlive,
scope: {'widgetInspectorService': widgetInspectorServiceRef.id!},
).then((ref) => ref.valueAsString!);
await safeEval(
'() async {'
' final reader = widgetInspectorService.toObject("$readerId", "$readerGroup") as List;'
' try {'
// Cast as dynamic so that it is possible to await Future<void>
' dynamic result = ($expression) as dynamic;'
' reader.add(result);'
' } catch (err, stack) {'
' reader.add(err);'
' reader.add(stack);'
' } finally {'
' postEvent("future_completed", {"future_id": $futureId, "client_id": $_clientId});'
' }'
'}()',
isAlive: isAlive,
scope: {
...?scope,
'postEvent': postEventRef.id!,
'widgetInspectorService': widgetInspectorServiceRef.id!,
},
);
await future;
final resultRef = await evalInstance(
'() {'
' final result = widgetInspectorService.toObject("$readerId", "$readerGroup") as List;'
' widgetInspectorService.disposeGroup("$readerGroup");'
' return result;'
'}()',
isAlive: isAlive,
scope: {'widgetInspectorService': widgetInspectorServiceRef.id!},
);
assert(resultRef.length == 1 || resultRef.length == 2);
if (resultRef.length == 2) {
throw FutureFailedException(
expression,
resultRef.elements![0],
resultRef.elements![1],
);
}
return resultRef.elements![0];
}