rustwasm / wasm-bindgen

Facilitating high-level interactions between Wasm modules and JavaScript
https://rustwasm.github.io/docs/wasm-bindgen/
Apache License 2.0
7.65k stars 1.05k forks source link

Stream from AsyncIterator in wasm-bindgen-futures #2399

Open olanod opened 3 years ago

olanod commented 3 years ago

AsyncIterator in JS is the native equivalent of Promise to represent multiple future values instead of one just as in Rust we have Future and Stream(soon in the standard library). Instead of creating a separate utility crate that converts async iterators to Rust streams I propose having that in function(e.g. iter_to_stream) in the wasm-bindgen-futures crate since the feature(can be behind a flag) is very "futures" related.

mattgibb commented 3 years ago

This would be awesome! Would it be possible to have the opposite too i.e. subscribe to a Rust Stream from Javascript via the registration of a callback/RxJS Observable or similar? Apologies in advance if this has an obvious solution, I am at the beginning of my journey with Rust...

I know already that you can call pre-existing global/static JS functions from Rust via an extern "C" block. However, it would be great to be able to manage subscriptions dynamically. I tried passing in a &js_sys::Function callback, but I couldn't get the callback to survive longer than the registration function:

use std::collections::HashMap;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
#[derive(Default)]
pub struct Subscribable<'a> { // compiler error that lifetimes aren't available 
  xs: Vec<u32>,
  subscribers: HashMap<u32, &'a js_sys::Function>,
  next_id: u32,
}

#[wasm_bindgen]
impl Subscribable {
  pub fn new() -> Self {
    Self {
      xs: vec![1, 2, 3],
      subscribers: HashMap::new(),
      next_id: 0,
    }
  }

  pub fn each(&self, callback: &js_sys::Function) {
    let this = JsValue::null();

    for &x in &self.xs {
      let x = JsValue::from(x);
      callback.call1(&this, &x);
    }
  }

  // subscriber can't survive past the end of this method
  pub fn subscribe(&mut self, subscriber: &js_sys::Function) -> u32 {
    let id = self.next_id;
    self.subscribers.insert(id, subscriber);
    self.next_id += 1;
    id
  }

  pub fn unsubscribe(&self, subscription: u32) {
    self.subscribers.remove(&subscription);
  }
}

Any pointers would be hugely appreciated!

sisou commented 1 year ago

@mattgibb Hi! Did you ever figure out how to do this? I'm at the same point now, having streams in Rust that I want to react to in JS, without a separate global listener function import. Either with callbacks like your code above, or by converting to an AsyncIterator.

kitty-eu-org commented 5 months ago

@mattgibb你好!你有想过如何做到这一点吗?我现在处于同一点,在 Rust 中有流,我想在 JS 中做出反应,而不需要单独的全局侦听器函数导入。使用像上面的代码这样的回调,或者转换为 AsyncIterator。

+1