异步正在成为背后的痛苦,因为我正在尝试制作一个不糟糕的可重用库

本文关键字:一个 痛苦 背后 因为 异步 | 更新日期: 2023-09-27 18:32:08

因为对API的Post请求需要在Windows Phone上异步运行,所以我正在努力创建一个精简的易于使用的库来与API交互。

问题是使用该库的人总是需要提供一个回调函数。

让我们看一些伪代码:

发布请求类来帮助我处理 POST 请求:

class PostRequest
{
    private Action<MemoryStream> Callback;
    public PostRequest(string urlPath, string data, Action<MemoryStream> callback)
    {
        Callback = callback;
        // Form the URI
        UriBuilder fullUri = new UriBuilder(urlPath);
        if (!string.IsNullOrEmpty(data))
            fullUri.Query = data;
        // Initialize a new WebRequest
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(fullUri.Uri);
        request.Method = "POST";
        // Set up the state object for the async request
        DataUpdateState dataState = new DataUpdateState();
        dataState.AsyncRequest = request;
        // Start the asynchronous request
        request.BeginGetResponse(new AsyncCallback(HandleResponse),
            dataState);
    }
    private void HandleResponse(IAsyncResult asyncResult)
    {
        // Get the state information
        DataUpdateState dataState = (DataUpdateState)asyncResult.AsyncState;
        HttpWebRequest dataRequest = (HttpWebRequest)dataState.AsyncRequest;
        // End the async request
        dataState.AsyncResponse = (HttpWebResponse)dataRequest.EndGetResponse(asyncResult);
        if (dataState.AsyncResponse.StatusCode.ToString() == "OK")
        {
            // Create a stream from the response
            Stream response = dataState.AsyncResponse.GetResponseStream();
            TextReader textReader = new StreamReader(response, true);
            string jsonString = textReader.ReadToEnd();
            MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
            // Send the stream through to the callback function
            Callback(stream);
        }
    }
}
public class DataUpdateState
{
    public HttpWebRequest AsyncRequest { get; set; }
    public HttpWebResponse AsyncResponse { get; set; }
}

API 访问对象类:

class APIAuthenticationCredentials
{
    public String Username { get; set; }
    public String Password { get; set; }
}
class APIAO
{
    private String AuthUrl = "http://api.example.com/";
    public static Auth Auth = new Auth();
    //... 
    public static void Authenticate( String data, APIAuthenticationCredentials credentials, Action<MemoryStream> callback )
    {
        PostRequest request = new PostRequest(AuthURL, data, callback);   
    }
    //... 
}

您会注意到,我必须一直传递一个回调函数,以便一旦我的 PostRequest 类中的 HandleResponse 方法返回数据,数据就会转发到某个控制器上,使屏幕对数据执行某些操作。目前,使用起来并不是很可怕:

private void DisplayData(MemoryStream stream)
{
    DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Auth));
    APIAO.Auth = (Auth)serializer.ReadObject(stream);
}
//...    
    APIAuthenticationCredentials credentials = new APIAuthenticationCredentials {
        Username = "whatever",
        Password = "whatever"
    }    
    APIAO.Authenticate( credentials, DisplayData );
//... 

问题是我想创建某种存储库样式模式...假设 API 返回了不同的 json 模型,一次调用返回了一系列产品......问题是我想创建一个可爱的存储库调用,例如:

IProductRepository productRepository = new ProductRepository();
productRepository.GetAll();

但是我也必须在其中放置一些 GOSH DARN 回调函数,这意味着 API 返回的任何对象类型的每个存储库方法都将具有此 MemoryStream 回调......如果我想更改该功能,我必须在任何地方更新这些东西哟。:(有没有人见过更好的方法来做这种废话。

这开始变得过于复杂

--哭

异步正在成为背后的痛苦,因为我正在尝试制作一个不糟糕的可重用库

使用较新的语言结构的更简单的答案是:

public static Task<string> GetData(string url, string data)
{
    UriBuilder fullUri = new UriBuilder(url);
    if (!string.IsNullOrEmpty(data))
        fullUri.Query = data;
    WebClient client = new WebClient();
    client.Credentials = CredentialCache.DefaultCredentials;//TODO update as needed
    return client.DownloadStringTaskAsync(fullUri.Uri);
}

在 4.0 项目中,您可以使用TaskCompletionSource将非任务异步模型转换为Task

public static Task<string> GetData2(string url, string data)
{
    UriBuilder fullUri = new UriBuilder(url);
    if (!string.IsNullOrEmpty(data))
        fullUri.Query = data;
    WebClient client = new WebClient();
    client.Credentials = CredentialCache.DefaultCredentials;//TODO update as needed
    var tcs = new TaskCompletionSource<string>();
    client.DownloadStringCompleted += (s, args) =>
    {
        if (args.Error != null)
            tcs.TrySetException(args.Error);
        else if (args.Cancelled)
            tcs.TrySetCanceled();
        else
            tcs.TrySetResult(args.Result);
    };
    client.DownloadStringAsync(fullUri.Uri);
    return tcs.Task;
}

调用方现在有一个表示此异步操作结果的Task<string>。 他们可以同步等待它并使用 Result 属性获取结果,他们可以添加一个回调,该回调将在操作完成后使用 ContinueWith 执行,或者他们可以在 async 方法中await任务,该方法在后台将连接该方法的其余部分作为该任务的延续, 但没有创建新方法甚至新范围,即

public static async Task Foo()
{
    string result = await GetData("http://google.com", "");
    Console.WriteLine(result);
}

这将启动异步任务,向该任务添加回调(或延续),以便在它运行时继续从中断的地方执行代码,此时它将结果写入控制台并将此方法返回的Task标记为已完成,以便随后将执行此方法的任何延续(允许组合async方法)。

相关文章: