在 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); }); },};
好处
当然是让代码逻辑更清晰,将回调写法,变成线性执行,对于复杂业务来说,能够很好让代码更可读和可理解
原文链接: https://blog.jgrass.cc/posts/destroy-callback/
本作品采用 「署名 4.0 国际」 许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。