如何使用async / await OnNext / OnError / OnCompleted方法实现IObserver?

时间:2016-02-03 17:44:37

标签: c# .net asynchronous task-parallel-library system.reactive

我正在尝试为System.Net.WebSocket编写一个扩展方法,它将使用Reactive Extensions(Rx.NET)将其转换为IObserver。您可以看到以下代码:

public static IObserver<T> ToObserver<T>(this WebSocket webSocket, IWebSocketMessageSerializer<T> webSocketMessageSerializer)
{
    // Wrap the web socket in an interface that's a little easier to manage
    var webSocketMessageStream = new WebSocketMessageStream(webSocket);

    // Create the output stream to the client
    return Observer.Create<T>(
        onNext:      async message => await webSocketMessageStream.WriteMessageAsync(webSocketMessageSerializer.SerializeMessage(message)),
        onError:     async error   => await webSocketMessageStream.CloseAsync(WebSocketCloseStatus.InternalServerError, string.Format("{0}: {1}", error.GetType(), error.Message)),
        onCompleted: async ()      => await webSocketMessageStream.CloseAsync(WebSocketCloseStatus.NormalClosure, "Server disconnected")
    );
}

此代码有效,但我担心在onNext,onError和onCompleted lambdas中使用async / await。我知道这会返回一个 async void lambda,这是不赞成的(有时会导致我已经遇到的问题)。

我一直在阅读Rx.NET文档以及互联网上的博客文章,我似乎无法找到在IObserver中使用async / await方法的正确方法(如果有的话)。有没有正确的方法来做到这一点?如果没有,那么我该如何解决这个问题?

1 个答案:

答案 0 :(得分:15)

订阅不采用if(rangeStart>rangeEnd){i--;}方法。那么这里发生的是你正在使用<html> <head> <script src="http://code.jquery.com/jquery-1.9.1.js"></script> <script> function conversion(n) { if (document.getElementById('mtokm').checked) { return (n/0.62137).toFixed(2); } else { return (n*0.62137).toFixed(2); } } function conversionTable(rangeStart, rangeEnd) { if(atLeastOneRadio() && rangeStart != false && rangeEnd != false) { divStr="<table border=1><tr><td>Miles</td><td>Kilometres</td></tr>";} for(i=rangeStart;i<=rangeEnd;i++) { if(i%2==0) { divStr+= "<tr bgcolor=\"yellow\"><td>" + i + "</td><td>" + conversion(i) + "</td></tr>"; } else { divStr+= "<tr bgcolor=\"green\"><td>" + i + "</td><td>" + conversion(i) + "</td></tr>"; } } document.getElementById("divResult").innerHTML=divStr; } else { alert("Please make sure you have entered an integer in both text boxes"); } } function getnputValue(input) { var nn = $("input[name=convert]:checked").val() var myInt = document.getElementById(input).value; if(myInt == parseInt(myInt)) return parseInt(myInt); else return false; } function check() { var radios = document.getElementsByName("choice"); $("input[name=convert]:checked").val() for (var i = 0, len = radios.length; i < len; i++) { if (radios[i].checked) { return true; } } return false; } function atLeastOneRadio() { return ($('input[type=radio]:checked').length > 0); } </script> </head> <body> <p> Start : <input type=textbox id=rangeTxt value=""/> End : <input type=textbox id=rangeTxt2 value=""/> <input type=radio name="convert" id="mtokm" value ="Miles to Kilometre"/> Miles to Kilometre <input type=radio name="convert" id="kmtom" value ="Kilometre to Miles"/> Kilometre to Miles <br> <br> <button onClick="conversionTable(getnputValue('rangeTxt'), getnputValue('rangeTxt2'))">Convert</button> </p> <div id="divResult"> </div> </body> </html> 中的“即发即忘”机制。问题是onNext消息将不再被序列化。

不应在Subscribe中调用async方法,而应将其包装到管道中以允许Rx等待它。

async voidasync上使用“即发即弃”是可以的,因为这些是onError调用的最后一件事。请注意,在onCompletedObservable返回之后,可以在完成之前清除与Observable相关联的资源。

我写了一个小扩展方法来完成所有这些:

onError

首先,我们将onCompleted方法转换为 public static IDisposable SubscribeAsync<T>(this IObservable<T> source, Func<T, Task> onNext, Action<Exception> onError, Action onCompleted) { return source.Select(e => Observable.Defer(() => onNext(e).ToObservable())).Concat() .Subscribe( e => { }, // empty onError, onCompleted); } ,桥接两个异步世界。这意味着async onNext内的异步/等待将得到尊重。接下来,我们将方法包装到Observable中,以便在创建时不会正确启动。相反,onNext只有在前一个完成时才会调用下一个deferred observable。

聚苯乙烯。我希望下一版本的Rx.Net在订阅中支持Defer