2014-04-23 6 views
5

Warum task nicht erwarten immer ?:IObservable <T> .ToTask <T> Methode gibt Task-Aktivierung wartet

var task = Observable 
    .FromEventPattern<MessageResponseEventArgs>(communicator, "PushMessageRecieved") 
    .Where(i => i.EventArgs.GetRequestFromReceivedMessage().Name == requestName) 
    .Select(i => i.EventArgs) 
    .RunAsync(System.Threading.CancellationToken.None) 
    .ToTask(); 

task.Wait(); 

Ich weiß "PushMessageRecieved" gebrannt; Ich kann einen Breakpoint auf Select Lambda setzen und ihn drücken. Aber task.Wait() bewegt sich nie.

Bessere Update:FirstAsync() ist das, was ich suche:

public static Task<MessageResponseEventArgs> HandlePushMessageRecievedAsync(this ICommunicator communicator, RequestName requestName) 
    { 
     if (communicator == null) return Task.FromResult<MessageResponseEventArgs>(null); 

     var observable = GetCommunicatorObservableForPushMessageReceived(communicator); 
     return observable 
      .Where(i => i.GetRequestFromReceivedMessage().Name == requestName) 
      .Select(i => i) 
      .FirstAsync() 
      .ToTask(); 
    } 

wo GetCommunicatorObservableForPushMessageReceived() ist:

static IObservable<MessageResponseEventArgs> GetCommunicatorObservableForPushMessageReceived(ICommunicator communicator) 
    { 
     if (communicatorObservableForPushMessageReceived == null) 
     { 
      communicatorObservableForPushMessageReceived = Observable 
       .FromEventPattern<MessageResponseEventArgs>(communicator, "PushMessageRecieved") 
       .Where(i => !IsPreviousMessage(i.EventArgs.GetRequestFromReceivedMessage().EventId)) 
       .Select(i => i.EventArgs); 
     } 

     return communicatorObservableForPushMessageReceived; 
    } 

Update: was etwas schrecklich ist, ist dies (aber es funktioniert) :

public static Task<MessageResponseEventArgs> HandlePushMessageRecievedAsync(this ICommunicator communicator, RequestName requestName) 
{ 
    if (communicator == null) return Task.FromResult<MessageResponseEventArgs>(null); 

    var completionSource = new TaskCompletionSource<MessageResponseEventArgs>(); 

    Observable 
     .FromEventPattern<MessageResponseEventArgs>(communicator, "PushMessageRecieved") 
     .Where(i => i.EventArgs.GetRequestFromReceivedMessage().Name == requestName) 
     .Select(i => i.EventArgs) 
     .ToEvent().OnNext += (args) => 
     { 
      if (args.Response.Errors != null && args.Response.Errors.Any()) 
      { 
       completionSource.TrySetException(args.Response.Errors.Select(j => new Exception(j.ErrorMessage))); 
      } 
      else 
      { 
       completionSource.TrySetResult(args); 
      } 
     }; 

    return completionSource.Task; 
} 
+0

Haben das 'Name' Eigenschaft Spiel' requestName'? – Lee

+0

@Lee: Wenn es das 'Select'-Lambda trifft, wie das OP sagt, dann muss es ja mit der Where-Klausel übereinstimmen. – StriplingWarrior

+1

Sollte ToTask nicht vor RunAsync auftreten? –

Antwort

8

Beide RunAsync und ToTask Ausbeute der allerletzte Wert in der beobachtbaren. Daher wird kein Wert erzeugt, bis das beobachtbare abgeschlossen ist. Aber Observables, die mit FromEventPattern erstellt wurden, werden normalerweise nicht abgeschlossen. Sie müssen sie mit etwas wie Take oder Until erzwingen.

Ich werde auch darauf hinweisen, dass RunAsync und ToTask im Wesentlichen redundant sind und es nicht notwendig ist, beides zu tun.

In Ihrem Fall, ich nehme an, Sie in dem ersten Wert wirklich interessiert sind, dass sie durch die Filter macht:

var task = Observable 
    .FromEventPattern<MessageResponseEventArgs>(communicator, "PushMessageRecieved") 
    .FirstAsync(i => i.EventArgs.GetRequestFromReceivedMessage().Name == requestName) 
    .Select(i => i.EventArgs) 
    .ToTask(); 

task.Wait(); 
+0

Viel Wertschätzung! Das funktioniert: 'Wähle (i => i) .Nehmen (1).ToTask() ' – rasx

+0

Das ist noch besser:' Wählen Sie (i => i) .FirstAsync(). ToTask() ' – rasx

+1

Warum fügen Sie hinzufügen' Select (i => i) '? Das hat keinen Zweck. – Brandon

2

Die Handler für das Ereignis PushMessageRecieved, das Sie beobachten, müssen im aktuellen Synchronisationskontext auf dem UI-Thread ausgeführt werden. Sie blockieren den UInthread (der den aktuellen Kontext darstellt), während Sie auf diese Aufgabe warten. Die Aufgabe kann nicht abgeschlossen werden, weil Sie darauf warten. Sie werden nie darauf warten, weil sie nicht ausgeführt werden kann. Sackgasse.

Sie sollten die Aufgabe nicht synchron blockieren, sondern asynchron Code als Fortsetzung dieser Aufgabe ausführen.

+0

Ich weiß zu schätzen, was Sie sagen, muss aber diesen Ratschlag auf den Fall eines Komponententests anwenden: kein UI-Thread. – rasx

+1

@rasx Als Erstes sollten Sie sicher sein, dass in Ihrem Komponententest kein Synchronisationskontext vorhanden ist. da kann es wohl eins geben. Und wenn es nicht ** gibt, dann sollte es ** sein, weil du sonst deine reale Umgebung nicht richtig nachgebildet hast. – Servy

Verwandte Themen