消灭事件回调,让其直接变成线性同步的代码风格
在 C# 和 Javascript 语言下,讨论如何封装事件返回的回调
问题场景
比如有一个库中,有一个 send 方法,用于发送命令,然后需要等待返回值,但 send 方法本身没有返回值,而是通过另外的事件来获取返回值。
伪代码如下:
// 通过事件回调来接收命令执行结果
foo.onDataReceive = (result) => { // receive result }
// 发送命令
foo.send("command")
这在使用上其实不是很方便,而且理解起来不直观,期望可以有如下的封装
var result = await myFoo.Send("command")
下面介绍在 C# 和 Javascript 中如何处理,在 C# 中使用的是 TaskCompletionSource 这个 API,Javascript 中使用的就是 Promise
尤其是 C# 中的这个 API,其实很简单,但是如果不知道,还真一时半会想不到特别优雅的方案。
在 Javascript 中,Promise 的提出,作用之一就是为了解决回调地狱,所以这个方案在 Javascript 显得就很自然。
csharp 版本
MessageSender
是原始 API, MyMessageSender
是封装。这里就可以直接使用 SendAsync
进行异步调用拿到结果,或者捕获异常。
class MyMessageSender
{
private TaskCompletionSource<string> _waitMessageSource = new();
private readonly MessageSender _messageSender;
public MyMessageSender()
{
_messageSender = new MessageSender();
_messageSender.MessageReceived += (sender, args) =>
{
if (args.ErrorCode == 0)
{
// 成功收到数据,则设置数据
_waitMessageSource.TrySetResult(args.Response);
}
else
{
// 没有成功,则抛出异常
_waitMessageSource.TrySetException(new MessageReceivedException(args.ErrorCode, args.Response));
}
};
}
/// <summary>
/// 发送请求数据,并获取响应
/// </summary>
/// <param name="request"></param>
/// <returns>响应数据</returns>
/// <exception cref="MessageReceivedException">数据接收出现错误</exception>
public async Task<string> SendAsync(string request)
{
_waitMessageSource = new();
_messageSender.Send(request);
return await _waitMessageSource.Task;
}
}
class MessageSender
{
public event EventHandler<MessageReceivedEventArgs> MessageReceived;
public void Send(string message)
{
}
}
class MessageReceivedEventArgs : EventArgs
{
public int ErrorCode { get; set; }
public string Response { get; set; } = "";
}
class MessageReceivedException(int code, string? message) : ApplicationException(message)
{
public int ErrorCode { get; set; } = code;
}
javascript 版本
js 中直接使用 Promise 来包装回调,这个是很自然的操作
sender = {
send(request, callback) {},
};
mySender = {
send(request) {
return new Promise((resolve, reject) => {
let callback = (response) => {
if (response.code == 0) {
resolve(response.message);
} else {
reject({
errorCode: response.code,
message: response.message,
});
}
};
sender.send(request, callback);
});
},
};
好处
当然是让代码逻辑更清晰,将回调写法,变成线性执行,对于复杂业务来说,能够很好让代码更可读和可理解
作者:
J.晒太阳的猫
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。