You really do not want to use this template:
void GetDataAsync(string parameters, Action<IEnumerable<object>> onSuccess, Action<Exception> onError);
Instead, you want to use this:
Task GetDataAsync(string parameters);
When you return Task you return an instance representing the asynchronous unit of work. From there, the consumer of the API can choose ContinueWith and decide what to do when it succeeds, or if there is an error.
However, there is a design flaw in your example. In a method called GetDataAsync I expect the data to be returned. This is not a problem, you can simply change the signature to:
Task<MyData> GetDataAsync(string parameters);
Now this returns a Task<T> , which you can use the Result property to get the result (it will be blocked if the task fails), or you can use the ContinueWith method again to process the data when the async operation is performed.
Alternatively, you can take an instance of the CancellationToken parameter to determine if you should cancel your operation:
Task<MyData> GetDataAsync(string parameters, CancellationToken cancellationToken);
Again, ContinueWith will allow you to specify an action to cancel.
Note that this is how the methods using await and async in Async CTP are modeled; they return Task or Task<T> ; doing the same in your code will allow you to be prepared when these language functions are baked.