2

Ich verwende eine Netzwerk-API, die mit Rückrufen funktioniert. Also im Grunde, ich habe ein paar Methodenaufrufe, dass ich in diesem 3rd-Party-Bibliothek verwenden müssen, die wie folgt aussehen:Wie wickeln Sie Async-Callback mit Observable oder Async/warten?

void SendNetworkRequest(string requestType, Action<Response> callback) 

ich den Code finden ein wenig verrückt bekommen, weil alle meine Methoden, die auf Netzwerk abhängen Ressourcen von dieser 3rd Party API müssen auch Callbacks selbst implementieren. Zum Beispiel habe ich kann in meiner Haupt-Szene will die Spieler Informationen und mein Code bekommen würde wie folgt aussehen:

void InitMainScene() 
{ 
     _networkHelper.SendNetworkRequest("GetPlayersInfo",OnPlayerInfoResponse); 
} 

void OnPlayerInfoResponse(Response response) 
{ 
    _playerInfo = response.Info; 
} 

Ich habe vor kurzem in RX bekommen und bin mit ihm abundently in meinem Code. Ich habe gelesen, lesen Sie etwas über async/erwarten. Ich habe viel experimentiert, vor allem mit RX, und habe versucht, mit Observable.FromAsync() zu arbeiten, konnte es aber nicht zum Laufen bringen.

Was fehlt mir hier? Wie kann ich Code schreiben, der sauberer ist und den Rückruf nicht benötigt, der RX oder async verwendet/wartet? Das folgende ist der pseudedocode, den ich suche:

 void InitMainScene() 
     { 
      _playerInfo = Overservable.DoMagicThing<Response>(() => { 
_networkHelper.SendNetworkRequest("GetPlayersInfo",(r) =>{return r;}); }); 
     } 

Antwort

1

Ich bin nicht sicher über RX. Sie können jedoch Rückruf-basierte SendNetworkRequest zu einer awaitable Aufgabe wie folgt konvertieren:

void SendNetworkRequest(string requestType, Action<Response> callback) 
     { 
      // This code by third party, this is here just for testing 
      if (callback != null) 
      { 
       var result = new Response(); 
       callback(result); 
      } 
     } 

     Task<Response> SendNetworkRequestAsync(string requestType) 
     { 
      return Task.Run(() => 
      { 
       var t = new TaskCompletionSource<Response>(); 

       SendNetworkRequest(requestType, s => t.TrySetResult(s)); 

       return t.Task; 
      }); 
     } 

Jetzt können Sie SendNetworkRequestAsync verbrauchen mit Asynchron/erwarten mehr natürlich

+0

Ihnen so vielen Dank! das ist toll! Ich werde heute damit spielen. Frage an dich - empfiehlst du diese Semantik in meinem Fall über die Overspervable?BTW - Ich habe noch nicht async/await verwendet, und tatsächlich mache ich das in Unity3d, das nur .NET 4.6 als experimentelles Feature unterstützt. – user3010678

+0

RX ist großartig, wenn Sie einen Strom von Ereignissen verarbeiten. In Ihrem Fall rufen Sie SendNetworkRequest auf und warten dann, bis die Aufgabe abgeschlossen ist. Also ja, async/wait ist in diesem Fall einfacher und vernünftiger als Rx. Sauber und einfacher –

1

Hier ist, wie es mit Rx zu tun. Ich tauschte aus Response für string nur so konnte ich kompilieren und zu testen, aber trivial .:

public IObservable<string> SendNetworkRequestObservable(string requestType) 
{ 
    return Observable.Create<string>(observer => 
    { 
     SendNetworkRequest(requestType, s => observer.OnNext(s)); 
     observer.OnCompleted(); 
     return Disposable.Empty; 
    }); 
} 

// Define other methods and classes here 
public void SendNetworkRequest(string requestType, Action<string> callback) 
{ 
    callback(requestType); // implementation for testing purposes 
} 
+0

Urrgh - Wenn du 'Disposable.Empty' jemals zurückbringst, machst du wahrscheinlich etwas falsch. – Enigmativity

+0

danke @Schlomo! Das hilft mir sehr! Ich werde damit spielen und versuchen, die Implikationen von Einweg besser zu verstehen. Leer, die Enigmativität beschreibt – user3010678

+0

@Enigmatismus, fühlen Sie sich frei, warum zu erweitern. Ich stimme nicht zu: In diesem Fall gibt es nichts zu entsorgen, aber ein Abonnement erfordert ein Einwegprodukt. Dies ist ein perfektes Beispiel dafür, warum 'Disposable.Empty' existiert. – Shlomo

1

zu tauschen zurück Hier ist mein nehmen dies mit Rx auf tun.

Innerhalb der NetworkHelper Klasse hinzufügen, um dieses Verfahren:

public IObservable<Response> SendNetworkRequestObservable(string requestType) 
{ 
    return 
     Observable 
      .Create<Response>(observer => 
      { 
       Action<Response> callback = null; 
       var callbackQuery = 
        Observable 
         .FromEvent<Response>(h => callback += h, h => callback -= h) 
         .Take(1); 
       var subscription = callbackQuery.Subscribe(observer); 
       this.SendNetworkRequest(requestType, callback); 
       return subscription; 
      }); 
} 

Diese beobachtbares schafft, dass Observable.FromEvent(...).Take(1) intern verwendet einen beobachtbaren auf erwartete einen einzigen Aufruf der zum SendNetworkRequest Methode übergeben Action<Response> callback basiert.

Das einzige Problem damit ist, dass der Aufruf auf den aktuellen Thread bis zum Abschluss auftritt. Wenn Sie diesen Code wollen auf einem Hintergrund-Thread laufen, aber das Ergebnis an den aktuellen Thread zurück, dann können Sie dies tun:

public IObservable<Response> SendNetworkRequestObservable(string requestType) 
{ 
    return 
     Observable 
      .Start(() => 
       Observable 
        .Create<Response>(observer => 
        { 
         Action<Response> callback = null; 
         var callbackQuery = 
          Observable 
           .FromEvent<Response>(h => callback += h, h => callback -= h) 
           .Take(1); 
         var subscription = callbackQuery.Subscribe(observer); 
         this.SendNetworkRequest(requestType, callback); 
         return subscription; 
        })) 
      .Merge(); 
} 

So oder so würde man es so nennen:

var _playerInfo = 
    _networkHelper 
     .SendNetworkRequestObservable("GetPlayersInfo") 
     .Wait(); 

Nur eine Randnotiz: Ihre DoMagicThing ist ein synchroner Anruf. In Rx ist das ein .Wait() Anruf, aber es ist besser, nur einen normalen .Subscribe zu verwenden, wenn Sie können.


Gemäß dem Kommentar, man könnte dies mit async:

async void InitMainScene() 
{ 
    _playerInfo = await _networkHelper.SendNetworkRequestObservable("GetPlayersInfo"); 
} 
+0

Vielen Dank für dieses Beispiel. Ich habe einige Zeit damit verbracht, es in meinem Spiel zum Laufen zu bringen. Es scheint jedoch, dass die Methode .Wait() Unity zum Einfrieren bringt und dann abstürzt. Ich muss graben, warum - auch async/warten unten Beispiel tut das gleiche. Vielen Dank für die Hilfe! Ich wollte Sie fragen - glauben Sie, async/warte ist eine bessere Lösung für dieses Problem, oder glauben Sie, dass Ihre Lösung für diesen speziellen Fall sauberer ist? Es ist schwer zu wissen, welcher in welcher Situation zu verwenden ist, da ich gerade mit beiden so unerfahren bin. Danke noch einmal! – user3010678

+0

@ user3010678 - Was Sie nicht tun sollten, ist '.Wait()'. Sie sollten einen regulären .Subscribe (...) '-Aufruf verwenden oder noch besser' async' verwenden und den Aufruf wie folgt aussehen lassen: 'var _playerInfo = awarte _networkHelper.SendNetworkRequestObservable (" GetPlayersInfo ");'. Dies funktioniert mit Observablen und gibt den letzten produzierten Wert zurück (wenn mehr als eins sind). – Enigmativity

Verwandte Themen