如何将JS Promise翻译为Rust

4

目前我正在编写一个纯Rust MQTT5库(我知道已经有现成的,但我更想学习Rust),我遇到了这个问题。

我使用最新的稳定版Rust和tokio 1.0.1。

当我发送一个数据包到服务器时,我通常期望从服务器获得响应(例如PingReq / PingAck,Ping / Pong)。

不考虑时间超时和数据包冲突的逻辑,我写了一个简化版本的JavaScript代码(因为我对它比较熟悉)。

这个逻辑在Rust和它的futures中怎么实现呢?或者更明确地说:我能否以某种方式重新创建awaitPackage + onIncomingPacket的resolve()回调函数行为?

class Client {
  awaitedPacketTypes = {};

  /**
   * a ping consist of a send ping and a receive pong
   */
  async ping(){
    await this.sendPacket("Ping");
    return await this.awaitPackage("Pong");
  }

  async sendPacket(packetType) { /*...*/ }
  
  /**
   * This expects a specific packet type to be received in the future
   * @param {*} packetType 
   */
  awaitPackage(packetType) {
    return new Promise((resolve, reject) => {
      this.awaitedPacketTypes[packetType] = {
        resolve,
        reject
      };
    });
  }

  /**
   * This gets called for every packet from the network side and calls the correct resolver if something waits for this packet type
   * @param {*} packet 
   */
  onIncomingPacket(packet) {
    if(this.awaitedPacketTypes[packet.type]) {
      this.awaitedPacketTypes[packet.type].resolve(packet);
      this.awaitedPacketTypes[packet.type] = undefined;
    } else {
      /*...*/
    }
  }
}
1个回答

5

更明确地说:我能以某种方式重新创建awaitPackage + onIncomingPacket的resolve()回调函数行为吗?

有点类似。Rust Future 只是“可以被轮询准备就绪”的东西,它比 JavaScript Promise 更低级。

虽然有一些库声称提供了类似于 JS Promise 的功能,但大多数异步库都可能提供一个不同命名的类似对象。例如,在 Tokio 中,你可能想要一个 oneshot channel,即一个只能发送单个值的通道,这将导致类似以下代码:

struct Packet { r#type: &'static str }
struct Client {
  awaited: Mutex<HashMap<&'static str, Sender<Packet>>>
}

impl Client {
    async fn ping(&self) -> Packet {
        self.send_packet("Pong").await;
        self.await_package("Pong").await.unwrap()
    }
    async fn send_packet(&self, _: &'static str) {}
    fn await_package(&self, packet_type: &'static str) -> Receiver<Packet> {
        let (tx, rx) = channel();
        self.awaited.lock().unwrap().insert(packet_type, tx);
        rx
    }
    fn on_incoming_packet(&self, packet: Packet) {
        if let Some(tx) = self.awaited.lock().unwrap().remove(packet.r#type) {
            tx.send(packet);
        }
    }
}

请注意,虽然这是 OP 的 JS 代码的正确等效形式,但更好的做法可能是排队传入的数据包,以防在接收到它们之前尚未调用 await_package 导致丢失。在 JS 中,我们通常可以依靠事件队列来处理这个问题。 - Bergi
很好在这里提到。我的逻辑已经包含了这个,但我把它删除了以简化问题。 - Snapstromegon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接