2

콜백과 함께 작동하는 네트워크 API를 사용하고 있습니다. 그러니까 기본적으로, 나는 방법의 무리 I는 다음과 같이이 제 3 자 라이브러리에서 사용할 필요가 통화를 할 :Observable 또는 async/await를 사용하여 비동기 콜백을 래핑하는 방법은 무엇입니까?

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

내가 코드를 약간 이상한 점점을 찾을 네트워크에 의존 내 모든 방법 때문에 이 제 3 자 API의 리소스도 콜백 자체를 구현해야합니다. 예를 들어, 내 주요 장면에서 나는 선수 정보를 얻을 할 수 있으며, 내 코드는 다음과 같이 보일 것입니다 :

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

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

나는 최근 RX에 입수했습니다 내 코드에서 abundently을 사용하고 있습니다. 나는 async/await에 관해 읽은 것을 읽었다. RX를 사용하여 꽤 많은 실험을 해본 결과 Observable.FromAsync()를 사용해 보았지만 작동하지 못했습니다 ..

여기서 무엇이 누락 되었습니까? RX 또는 async/await를 사용하여 콜백이 필요없는 코드를 더 깨끗하게 작성할 수 있습니까? 다음은 내가 찾는 psuedocode입니다 :

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

답변

1

RX에 대해서는 확실하지 않습니다. 그러나이 같은 awaitable 작업에 콜백 기반 SendNetworkRequest을 변환 할 수 있습니다

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; 
      }); 
     } 

이제 당신은/비동기로 SendNetworkRequestAsync을 소비 할 수 있습니다 여기에

+0

너무 감사합니다! 이게 굉장합니다! 나는 오늘 이것으로 놀 것이다. 당신을위한 질문 - 내 의미에서 Overservable보다이 의미를 권장합니까?BTW - 아직 async/await를 사용하지 않았고 사실, 실험적 기능으로 .net 4.6 만 지원하는 Unity3d에서이 작업을 수행하고 있습니다. – user3010678

+0

이벤트 스트림을 처리하는 경우 RX가 좋습니다. 귀하의 경우에는 SendNetworkRequest를 호출 한 다음 작업이 완료 될 때까지 기다려야합니다. 그래서 예, 비동기/대기는이 경우 Rx보다 쉽고 합리적입니다. 깨끗하고 쉽게 –

1

가 수신와 함께 할 방법은 더 자연스럽게 기다리고 있습니다. 난 그냥 내가 컴파일 및 테스트,하지만 사소한 다시 :

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 - 당신이 'Disposable.Empty'를 반환하는 경우 아마도 당신은 뭔가 잘못하고있는 것입니다. – Enigmativity

+0

감사합니다 @Schlomo! 이것은 나를 많이 돕는다! 나는 이것과 함께 놀고 Disposable.Empty의 의미를 더 잘 이해하려고 노력한다. Enigmativity가 설명하는 – user3010678

+0

@Enigmativity는 왜 확장해야 하는지를 느낀다. 나는 동의하지 않는다 :이 경우에는 처분 할 것이 없다. 그러나 구독하려면 일회용품이 필요하다. 이것은 'Disposable.Empty'가 존재하는 이유의 완벽한 예입니다. – Shlomo

1

다음은 수신과 함께이 일에 내 걸릴이야를 교환 할 수 string에 대한 Response을 교환. 이 내부적으로 SendNetworkRequest 메서드에 전달 된 Action<Response> callback에 하나의 전화를 기다리고 기반으로 관찰을 만들 Observable.FromEvent(...).Take(1)를 사용하는 관측을 생성

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; 
      }); 
} 

다음 NetworkHelper 클래스 내부

이 방법을 추가 할 수 있습니다.

유일한 문제는 현재 스레드에서 호출이 완료 될 때까지 발생한다는 것입니다. 당신이 백그라운드 스레드에서 실행되지만 현재 스레드에 결과를 반환이 코드를 원하는 경우에, 당신은이 작업을 수행 할 수 있습니다

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

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(); 
} 

어느 쪽이든 당신은 다음과 같이 호출 것

그냥 쪽지 : DoMagicThing은 동기 호출입니다. Rx의 경우 .Wait() 전화가 있지만 가능하면 정상적인 .Subscribe을 사용하는 것이 좋습니다.


코멘트 당, 당신이 async이 할 수 있습니다 :

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

이 예를 들어 주셔서 감사합니다. 나는 내 게임에서 작동하도록 노력하면서 시간을 보내고있다. .Wait() 메서드로 인해 Unity가 동결되어 충돌하는 것으로 보인다. 나는 왜 그런지 파고들 필요가있다. 심지어 아래의 async/await조차도 똑같은 일을한다. 도와 주셔서 대단히 감사합니다! 나는 당신에게 묻고 싶다 - 비동기/대기가이 문제에 대한 더 나은 해결책이라고 생각 하는가? 아니면이 특정한 경우에 당신의 솔루션이 더 깨끗하다고 ​​생각 하는가? 어느 상황에서 어떤 것을 사용할지를 아는 것은 어렵습니다. 저는 지금이 둘 모두와 함께 그렇게 경험이 없습니다. 다시 한 번 감사드립니다! – user3010678

+0

@ user3010678 - 당신이하지 말아야 할 것은'.Wait()'을 사용하는 것입니다. 일반 .Subscribe (...)'호출을 사용하거나 더 나은 방법은'async'를 사용하여 호출을 다음과 같이 만들어야합니다 :'var _playerInfo = await _networkHelper.SendNetworkRequestObservable ("GetPlayersInfo");'. 그것은 observables와 함께 작동하고 생성 된 마지막 값을 반환합니다 (둘 이상인 경우). – Enigmativity