Add AsyncTaskCodeActivity type
Foxtrek64 opened this issue · 2 comments
Currently for those wishing to build a custom asynchronous activity, one must implement AsyncCodeActivity
and work with the NetFx 3 async library, implementing BeginExecute
and EndExecute
. For many newer developers, this model is foreign and may be difficult to find adequate documentation of how to implement an activity using this model.
I would like to propose a new type, AsyncTaskCodeActivity
, which instead exposes an abstract ExecuteAsync()
method which returns a Task
or Task<T>
as appropriate. This can reuse much of the existing code available in the Windows Workflow Foundation library, such as AsyncCodeActivityContext
.
Proposed API Changes
public abstract class AsyncTaskCodeActivity : AsyncCodeActivity
{
protected abstract Task ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken);
}
public abstract class AsyncTaskCodeActivity<TValue> : AsyncCodeActivity<TValue>
{
protected abstract Task<TValue> ExecuteAsync(AsyncCodeActivityContext context, CancellationToken cancellationToken);
}
Considerations/changes:
- Should
System.ValueTask
be used instead ofSystem.Task
? If a user wraps a MemoryStream or some other existing ValueTask that can return synchronously,ValueTask
allows us to wrap the return value and avoid allocating. There are however a couple of potential downsides:ValueTask
/ValueTask<T>
is less usable than itsTask
/Task<T>
counterpart. However, sinceValueTask
/ValueTask<T>
is best used in environments where it is simply going to be awaited, this makes it a particularly good candidate for UiPath. If it is being called outside of the UiPath code runner, then it is likely being done in a unit test by the developer who is building the activity. It will be on them to handle it appropriately.ValueTask
/ValueTask<T>
is larger thanTask
/Task<T>
when wrappingTask
/Task<T>
. This is a measurable difference in microbenchmarks, however for utilization in an environment like UiPath, I doubt it will make a big difference.
- Is
AsyncCodeActivity
the correct target for extension? Should it instead extendActivity
likeAsyncCodeActivity
does? The way my current implementation ofAsyncTaskCodeActivity
works, uses BeginExecute to start the task and uses a TaskCompletionSource to track the lifetime of the task. Then it uses EndExecute to return the value of the task. In the case ofAsyncCodeActivity
(without a return type), it uses an emptyVoidResult
struct as its result (since I can't usevoid
as a return type). This type works similarly to MediatR'sUnit
type.
Should there be an interest in this change, I would be happy to submit a PR.
Sounds good! A PR is welcome.
- We're not a high performance library, so
Task
seems best. - It seems there's useful stuff to reuse in
AsyncCodeActivity
. But make sure to sealBeginExecute/EndExecute
. Also see this.
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.