如何使用Observable或async / await包装异步回调?

时间:2017-10-17 01:54:07

标签: c# asynchronous async-await system.reactive reactive-programming

我正在使用可与回调配合使用的网络API。所以基本上,我有一堆方法调用,我需要在这个第三方库中使用,如下所示:

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

我发现代码有点古怪,因为我依赖于来自第三方API的网络资源的所有方法也需要自己实现回调。例如,在我的主场景中,我可能想要获取玩家信息,我的代码看起来像这样:

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

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

我最近进入了RX并在我的代码中使用它。我读过一些关于async / await的内容。我已经尝试了很多,特别是对于RX,并尝试使用Observable.FromAsync()但无法使其工作..

我在这里缺少什么?如何编写更清晰且不需要使用RX或async / await进行回调的代码?以下是我正在寻找的伪代码:

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

3 个答案:

答案 0 :(得分:1)

我不确定RX。但是,您可以将基于回调的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;
            });
        }

现在您可以使用async / await更自然地使用SendNetworkRequestAsync

答案 1 :(得分:1)

以下是如何使用Rx完成的。我为Response换了string只是为了让我可以编译和测试,但是可以轻易地换回来。:

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
}

答案 2 :(得分:1)

这是我对Rx这样做的看法。

NetworkHelper类中添加此方法:

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

这会创建一个observable,在内部使用Observable.FromEvent(...).Take(1)创建一个observable,基于期望对传递给Action<Response> callback方法的SendNetworkRequest进行单次调用。

唯一的问题是调用发生在当前线程完成之前。如果您希望此代码在后台线程上运行,但将结果返回到当前线程,则可以执行以下操作:

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

你可以这样称呼它:

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

只是旁注:您的DoMagicThing是同步通话。在Rx中进行.Wait()通话,但最好只使用普通.Subscribe

根据评论,您可以使用async

执行此操作
async void InitMainScene()
{
    _playerInfo = await _networkHelper.SendNetworkRequestObservable("GetPlayersInfo");
}