Waiting for an IAsyncResult method waiting for another IAsyncResult (Chaining)

(can only use .NET 3.5 stuff, so no tasks, no active extensions)

I have what I thought was a simple case, but I am puzzled by this.

In short, I am returning the BeginGetRequestStream IAsyncResult to the BeginMyOperation () caller, and I want to really send back the IAsyncResult BeginGetResponse, which is called when EndGetRequestStream is called.

So I wonder how I

public IAsyncResult BeginMyOperation(...) { HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(requestUri); webRequest.Method = "POST"; // This is the part, that puzzles me. I don't want to send this IAsyncResult back. return webRequest.BeginGetRequestStream(this.UploadingStreamCallback, state); } // Only want this to be called when the EndGetResponse is ready. public void EndMyOperation(IAsyncResult ar) { } private IAsyncResult UploadingStreamCallback(IAsyncResult asyncResult) { using (var s = state.WebRequest.EndGetRequestStream(asyncResult)) { using (var r = new BinaryReader(state.Request.RequestData)) { byte[] uploadBuffer = new byte[UploadBufferSize]; int bytesRead; do { bytesRead = r.Read(uploadBuffer, 0, UploadBufferSize); if (bytesRead > 0) { s.Write(uploadBuffer, 0, bytesRead); } } while (bytesRead > 0); } } // I really want to return this IAsyncResult to the caller of BeginMyOperation return state.WebRequest.BeginGetResponse(new AsyncCallback(state.Callback), state); } 
+4
source share
5 answers

I think the easiest way to solve this is to use Task wrappers. In particular, you can complete TaskCompletionSource when BeginGetResponse completes. Then return Task for this TaskCompletionSource . Note that Task implements IAsyncResult , so your client code will not change.

Personally, I would take the next step:

  • Wrap BeginGetRequestStream in Task (using FromAsync ).
  • Create a continuation for Task that processes the request and wraps BeginGetResponse in Task (again, using FromAsync ).
  • Create a continuation for this second Task , which completes the TaskCompletionSource .

IMHO, exceptions, and result values ​​are more naturally handled by Task than IAsyncResult .

+3
source

What you are trying to do is doable, but you need to create a new implementation of IAsyncResult (something like "CompositeResult" that looks at the first IAsyncResult and then launches the second call).

However, this task is actually much simpler using Reactive Extensions - in this case you should use Observable.FromAsyncPattern to convert your Begin / End methods to Func, which returns IObservable (which also represents the result of async), then chain with SelectMany:

 IObservable<Stream> GetRequestStream(string Url); IObservable<bool> MyOperation(Stream stream); GetRequestStream().SelectMany(x => MyOperation(x)).Subscribe(x => { // When everything is finished, this code will run }); 
+2
source

I understand that this question is almost one year old, but if the restrictions still exist, there is an option on .NET 3.5 that makes it easy to create asynchronous operations. Take a look at the Jeff Richter PowerThreading library . In the Wintellect.PowerThreading.AsyncProgModel namespace Wintellect.PowerThreading.AsyncProgModel you will find several variations of the AsyncEnumerator class that you can use with sequence generators to write async code as if it were sequential.

Its essence is that you write your asynchronous code as the body of a sequence generator that returns an IEnumerator<int> , and whenever you call the async method, you call yield return with the number of async operations to wait, the library processes the gory details.

For example, to send some data to a URL and return the contents of the result:

 public IAsyncResult BeginPostData(string url, string content, AsyncCallback callback, object state) { var ae = new AsyncEnumerator<string>(); return ae.BeginExecute(PostData(ae, url, content), callback, state); } public string EndPostData(IAsyncResult result) { var ae = AsyncEnumerator<string>.FromAsyncResult(result); return ae.EndExecute(result); } private IEnumerator<int> PostData(AsyncEnumerator<string> ae, string url, string content) { var req = (HttpWebRequest)WebRequest.Create(url); req.Method = "POST"; req.BeginGetRequestStream(ae.End(), null); yield return 1; using (var requestStream = req.EndGetRequestStream(ae.DequeAsyncResult())) { var bytes = Encoding.UTF8.GetBytes(content); requestStream.BeginWrite(bytes, 0, bytes.Length, ae.end(), null); yield return 1; requestStream.EndWrite(ae.DequeueAsyncResult()); } req.BeginGetResponse(ae.End(), null); yield return 1; using (var response = req.EndGetResponse(ae.DequeueAsyncResult())) using (var responseStream = response.GetResponseStream()) using (var reader = new StreamReader(responseStream)) { ae.Result = reader.ReadToEnd(); } } 

As you can see, the private PostData() process is responsible for the bulk of the work. There are three asynchronous programming methods, as evidenced by the three yield return 1 . With this template, you can bind as many async methods as you want, and just return one IAsyncResult caller.

+2
source

I really don't understand what you are trying to achieve, but I think you should review the code. An IAsyncResult instance is an object that allows you to handle calls to asynchronous methods, and they are created when you make an asynchronous call through BeginXXX.

In your example, you basically want to return an instance of IAsyncResult that does not yet exist .

I really don't know what the problem you are trying to solve is, but maybe one of these approaches works better for you:

  • Encapsulate this code in the class and tell the users of your code that the operation has completed subscribing to the event .
  • Encapsulate this code in the class and force users to provide a callback delegate, which will be called when work is completed. You can pass the results as a parameter to this callback

Hope this helps!

+1
source

First, get the implementation code for AsyncResultNoResult and AsyncResult<TResult> from Jeffrey Richter’s MSDN magazine article, “ Implementing the Asynchronous CLR Programming Model .”

Once you have these base classes, you can relatively easily implement your own async result. In this example, I will use your base code to run a web request, and then get the response as a single async operation, consisting of several internal async operations.

 // This is the class that implements the async operations that the caller will see internal class MyClass { public MyClass() { /* . . . */ } public IAsyncResult BeginMyOperation(Uri requestUri, AsyncCallback callback, object state) { return new MyOperationAsyncResult(this, requestUri, callback, state); } public WebResponse EndMyOperation(IAsyncResult result) { MyOperationAsyncResult asyncResult = (MyOperationAsyncResult)result; return asyncResult.EndInvoke(); } private sealed class MyOperationAsyncResult : AsyncResult<WebResponse> { private readonly MyClass parent; private readonly HttpWebRequest webRequest; private bool everCompletedAsync; public MyOperationAsyncResult(MyClass parent, Uri requestUri, AsyncCallback callback, object state) : base(callback, state) { // Occasionally it is necessary to access the outer class instance from this inner // async result class. This also ensures that the async result instance is rooted // to the parent and doesn't get garbage collected unexpectedly. this.parent = parent; // Start first async operation here this.webRequest = WebRequest.Create(requestUri); this.webRequest.Method = "POST"; this.webRequest.BeginGetRequestStream(this.OnGetRequestStreamComplete, null); } private void SetCompletionStatus(IAsyncResult result) { // Check to see if we did not complete sync. If any async operation in // the chain completed asynchronously, it means we had to do a thread switch // and the callback is being invoked outside the starting thread. if (!result.CompletedSynchronously) { this.everCompletedAsync = true; } } private void OnGetRequestStreamComplete(IAsyncResult result) { this.SetCompletionStatus(result); Stream requestStream = null; try { stream = this.webRequest.EndGetRequestStream(result); } catch (WebException e) { // Cannot let exception bubble up here as we are on a callback thread; // in this case, complete the entire async result with an exception so // that the caller gets it back when they call EndXxx. this.SetAsCompleted(e, !this.everCompletedAsync); } if (requestStream != null) { this.WriteToRequestStream(); this.StartGetResponse(); } } private void WriteToRequestStream(Stream requestStream) { /* omitted */ } private void StartGetResponse() { try { this.webRequest.BeginGetResponse(this.OnGetResponseComplete, null); } catch (WebException e) { // As above, we cannot let this exception bubble up this.SetAsCompleted(e, !this.everCompletedAsync); } } private void OnGetResponseComplete(IAsyncResult result) { this.SetCompletionStatus(result); try { WebResponse response = this.webRequest.EndGetResponse(result); // At this point, we can complete the whole operation which // will invoke the callback passed in at the very beginning // in the constructor. this.SetAsCompleted(response, !this.everCompletedAsync); } catch (WebException e) { // As above, we cannot let this exception bubble up this.SetAsCompleted(e, !this.everCompletedAsync); } } } } 

Some notes:

  • You cannot throw an exception in the context of an asynchronous callback. You will break your application because there is no one to process it. Instead, always perform an async operation with an exception. This ensures that the caller sees the exception in the EndXxx call and can handle it accordingly.
  • Suppose that everything that BeginXxx can throw can also be dropped from EndXxx. In the example above, the example assumes that a WebException can occur anyway.
  • The “completed synchronously” state setting is important when the caller performs an asynchronous cycle. This will tell the caller when they need to return from their asynchronous callback to avoid stacking. More information on this can be found here on the blog Michael Marucheck " Asynchronous Programming in Indigo " (see "Stacking the Stack" section).

Asynchronous programming is not the easiest thing, but very powerful when you understand the concepts.

0
source

Source: https://habr.com/ru/post/1346495/


All Articles