Troble in saving response of server in a variable outside of wasm_future scope in yew

Hello

I am trying to save the response of my server into a variable outside of wasm_futures scope to return it to update function.but the value of target variable (response) won't change.

I am guessing that the problem is in async scope, because send_request function won't wait for updated response value and just returning the default vale (noenter).

do you know how can I solve this problem?

here is my whole code:

#[derive(Properties,PartialEq)]
pub struct Props{
    pub url:String,

}

pub struct Page{
    inner_text:String
}
pub enum Msg{
    GetRequest,
    SendRequest
}
impl Component for Page {
    type Message=Msg;
    
    type Properties=Props;

    fn create(ctx: &Context<Self>) -> Self {
        log!(ctx.props().url.clone());
        let url = ctx.deref().props().url.clone();
        log!(&url);
        let request_handle = {
            let link = ctx.link().clone();

            Interval::new(5000,move|| { link.send_message(Msg::SendRequest)})
        };
        request_handle.forget();
        Self{inner_text:"".to_owned()}
    }
    
    fn view(&self, ctx: &Context<Self>) -> Html {
        
        html! {
        <>
        <ToolBar url={"test"}/> 
        <div>
            <div class="container-fluid">
                <textarea id="textarea" class={classes!("textarea",css!("white-space:pre-wrap;"))} value={self.inner_text.clone()} >
                   
                </textarea>
            </div>
        </div>
        </>
     }
    }
    fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
        match msg {
            Msg::GetRequest => todo!(),
            Msg::SendRequest =>{
                let response = self.send_request( ctx.props().url.clone());
                self.inner_text=response.clone();

                true
            },
        }
        
    }
}


impl Page {
    
    pub fn send_request(self: &mut Self ,url:String)->String{
    let url = url.clone();
    let response = Arc::new(Mutex::new(String::from("noenter")));
    let response_clone = Arc::clone(&response);
    wasm_bindgen_futures::spawn_local(async move{
        let _url = format!("http://127.0.0.1:3000/{}",url);
        // log!(&_url);
        // log!("sdfasdfas");
        let res = Request::get(_url.as_str())
        .send()
        .await
        .unwrap()
        .text()
        .await
        .unwrap();
        let mut response = response_clone.lock().unwrap();
        *response = res;
    });
    let response = response.lock().unwrap();
    let x = response.clone();
    x
    }
}

well, it being async means either you async all the way up the call chain (see what color is your function?), or you use other mechenism to notify the future is finished

in this case, you probably could extend your Message type with a new variant, and send a message in the async closure after the url is loaded, and receive the actual response in the message handler. though I might be wrong, as I'm not an web developer and I don't know what library you are using, you probably could get better support from the community of that specific library/framework.

1 Like

thanks for your reply,

yeah exactly!

I figured out that it is not possible to use ctx.props() inside an async closure. but it is possible to clone ctx.link() and then use send_message() to call update function and save data inside of it.
So I changed my code into this:
ā€ā€```
pub enum Msg{
GetRequest(String),
CheckRequest,
firstRequest,
}
impl Component for Page {
type Message=Msg;

type Properties=Props;

fn create(ctx: &Context<Self>) -> Self {
    let url = ctx.deref().props().url.clone();
    let request_handle = {
        let link = ctx.link().clone();

        Interval::new(5000,move|| { link.send_message(Msg::CheckRequest)})
    };
    request_handle.forget();

    Self{inner_text:"".to_owned(),input_ref:NodeRef::default()}

}

fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
    match msg {
        Msg::GetRequest(res) => {
            let text = self.fix_received_response_text(&mut res.to_string());
            self.inner_text=text.clone();
            true
        },
        Msg::CheckRequest =>{
            if let Some(input) = self.input_ref.cast::<HtmlInputElement>() {
                let value = input.value();
                let response = self.check_send_or_receive_data(ctx, ctx.props().url.clone(),value);
            }

            false
        },


        _ => {false}
    }

}
fn view(&self, ctx: &Context<Self>) -> Html {

    html! {
    <>
    <ToolBar url={ctx.props().url.clone()}/>
    <div>
        <div class="container-fluid">
            <textarea id="textarea" ref={self.input_ref.clone()} class={classes!("textarea")} value={self.inner_text.clone()} >

            </textarea>
        </div>
    </div>
    </>
 }
}
fn rendered(&mut self, ctx: &Context<Self>, first_render: bool) {
    if first_render{
        self.receive_text(ctx,&ctx.props().url.clone());
    }
}

}

impl Page {
fn fix_received_response_text(&mut self, text:&mut String) ->String {
log!("aasdfasdfasdf");
let mut text = text.clone();
if !text.is_empty() {
let res = text.replace('"',"");
let res = res.replace("\r", "\r");
let res = res.replace("\n", "\n");
text=res;
}
log!(&text);
text
}
fn check_newtext_and_lasttext(&mut self,new_text:String,last_text:String)->bool{
let binding = self.fix_received_response_text(&mut new_text.clone());
let new_text:String = binding.chars().filter(|c|!c.is_whitespace()).collect();
let binding = self.fix_received_response_text(&mut last_text.clone());
let last_text:String= binding.chars().filter(|c|!c.is_whitespace()).collect();
log!(format!("{}/{}",new_text.len(),last_text.len()));
log!("------------------------------");
new_text.eq(&last_text)
}
pub fn check_send_or_receive_data(&mut self, ctx:&Context, url:String,textarea_value:String){

    if self.check_newtext_and_lasttext(textarea_value.clone(),self.inner_text.clone()){
        log!("receive");

        self.receive_text(ctx,&url.clone())

    }
    else {
        log!("send");
        self.send_text(ctx,&url.clone(),textarea_value);
    }
}
fn send_text(&mut self,ctx:&Context<Self>, url:& String,data:String){
    log!("send new data");
    let url = url.clone();
    let link = ctx.link().clone();
    let current_text = self.inner_text.clone();
    wasm_bindgen_futures::spawn_local(async move{
        let _url = format!("{}/{}",SERVER_URL,url);
        let body = json!(
            {
                "text":data
            }
        );
        let res = Request::post(_url.as_str())
            .body(body.to_string())
            .send()
            .await
            .unwrap();

        if !res.ok(){
            log!("http request failed");
        }
    });
}
fn receive_text(&mut self,ctx:&Context<Self>, url:&String){
    let url = url.clone();
    let link = ctx.link().clone();
    let current_text = self.inner_text.clone();
    wasm_bindgen_futures::spawn_local(async move{
        let _url = format!("{}/{}",SERVER_URL,url);

        let res = Request::get(_url.as_str())
            .send()
            .await
            .unwrap();

        if res.ok(){
            let received_text = res.text().await.unwrap();
            if received_text!=current_text{
                link.send_message(Msg::GetRequest(received_text));
            }
        }
    });

}

}
ā€ā€ā€ā€ā€ā€ā€ā€```

I've changed code a lot from the time that I created this topic.
But the main thing that I did is using ā€ā€ctx.link().send_message(Msg::GetRequest(received_text))
and then saving the data in msg match of update function : let text =self.fix_received_response_text(&mut res.to_string());

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.