Repository: DioxusLabs/dioxus (language: rust)

Issue:
Returning values from eval fails on dioxus web
**Problem**

Returning a value in `document::eval` only works on dioxus desktop and fails in dioxus web. This is causing https://github.com/DioxusLabs/docsite/issues/398 (also mentioned in https://github.com/DioxusLabs/dioxus/issues/3574)

**Steps To Reproduce**

1) Run this code in dioxus desktop and dioxus web and click the button:
```rust
use dioxus::prelude::*;

fn main() {
    dioxus::launch(App);
}

#[component]
fn App() -> Element {
    rsx! {
        button {
            onclick: |_| async move {
                let output = document::eval("return 'Hello world!';").await.unwrap();
                assert_eq!(output, "Hello world!");
            },
            "Click me"
        }
    }
}
```

2) Observe the web version panics.

**Expected behavior**

Returning a value from eval should work the same on all webview based platforms
**Screenshots**

**Environment:**
 - Dioxus version:  `0.6.2`
 - Rust version:    `nightly`
 - OS info:         MacOS
 - App platform:    `web`

**Questionnaire**
<!-- If you feel up to the challenge, please uncomment one of the lines below: -->

<!-- I'm interested in fixing this myself but don't know where to start -->
I would like to fix and I have a solution
<!-- I don't have time to fix this right now, but maybe later -->



Discussion / hints:

Fix patch:
diff --git a/packages/web/src/document.rs b/packages/web/src/document.rs
index 66853c4e11..b97402e7e5 100644
--- a/packages/web/src/document.rs
+++ b/packages/web/src/document.rs
@@ -5,14 +5,17 @@ use dioxus_document::{
     ScriptProps, StyleProps,
 };
 use dioxus_history::History;
+use futures_util::FutureExt;
 use generational_box::{AnyStorage, GenerationalBox, UnsyncStorage};
 use js_sys::Function;
 use serde::Serialize;
 use serde_json::Value;
 use std::future::Future;
 use std::pin::Pin;
+use std::result;
 use std::{rc::Rc, str::FromStr};
 use wasm_bindgen::prelude::*;
+use wasm_bindgen_futures::JsFuture;
 
 use crate::history::WebHistory;
 
@@ -131,10 +134,9 @@ impl Document for WebDocument {
 
 /// Required to avoid blocking the Rust WASM thread.
 const PROMISE_WRAPPER: &str = r#"
-    return new Promise(async (resolve, _reject) => {
+    return (async function(){
         {JS_CODE}
-        resolve(null);
-    });
+    })();
 "#;
 
 type NextPoll = Pin<Box<dyn Future<Output = Result<serde_json::Value, EvalError>>>>;
@@ -143,7 +145,7 @@ type NextPoll = Pin<Box<dyn Future<Output = Result<serde_json::Value, EvalError>
 struct WebEvaluator {
     channels: WeakDioxusChannel,
     next_future: Option<NextPoll>,
-    result: Option<Result<serde_json::Value, EvalError>>,
+    result: Pin<Box<dyn Future<Output = result::Result<Value, EvalError>>>>,
 }
 
 impl WebEvaluator {
@@ -163,7 +165,15 @@ impl WebEvaluator {
         let result = match Function::new_with_args("dioxus", &code).call1(&JsValue::NULL, &channels)
         {
             Ok(result) => {
-                if let Ok(stringified) = js_sys::JSON::stringify(&result) {
+                let future = js_sys::Promise::resolve(&result);
+                let js_future = JsFuture::from(future);
+                Box::pin(async move {
+                    let result = js_future.await.map_err(|e| {
+                        EvalError::Communication(format!("Failed to await result - {:?}", e))
+                    })?;
+                    let stringified = js_sys::JSON::stringify(&result).map_err(|e| {
+                        EvalError::Communication(format!("Failed to stringify result - {:?}", e))
+                    })?;
                     if !stringified.is_undefined() && stringified.is_valid_utf16() {
                         let string: String = stringified.into();
                         Value::from_str(&string).map_err(|e| {
@@ -171,23 +181,20 @@ impl WebEvaluator {
                         })
                     } else {
                         Err(EvalError::Communication(
-                            "Failed to stringify result".into(),
+                            "Failed to stringify result - undefined or not valid utf16".to_string(),
                         ))
                     }
-                } else {
-                    Err(EvalError::Communication(
-                        "Failed to stringify result".into(),
-                    ))
-                }
+                })
+                    as Pin<Box<dyn Future<Output = result::Result<Value, EvalError>>>>
             }
-            Err(err) => Err(EvalError::InvalidJs(
+            Err(err) => Box::pin(futures_util::future::ready(Err(EvalError::InvalidJs(
                 err.as_string().unwrap_or("unknown".to_string()),
-            )),
+            )))),
         };
 
         owner.insert(Box::new(Self {
             channels: weak_channels,
-            result: Some(result),
+            result,
             next_future: None,
         }) as Box<dyn Evaluator>)
     }
@@ -197,13 +204,9 @@ impl Evaluator for WebEvaluator {
     /// Runs the evaluated JavaScript.
     fn poll_join(
         &mut self,
-        _cx: &mut std::task::Context<'_>,
+        cx: &mut std::task::Context<'_>,
     ) -> std::task::Poll<Result<serde_json::Value, EvalError>> {
-        if let Some(result) = self.result.take() {
-            std::task::Poll::Ready(result)
-        } else {
-            std::task::Poll::Ready(Err(EvalError::Finished))
-        }
+        self.result.poll_unpin(cx)
     }
 
     /// Sends a message to the evaluated JavaScript.


Test patch:
diff --git a/packages/playwright-tests/web/src/main.rs b/packages/playwright-tests/web/src/main.rs
index 58163ebd78..6e385959cb 100644
--- a/packages/playwright-tests/web/src/main.rs
+++ b/packages/playwright-tests/web/src/main.rs
@@ -25,6 +25,13 @@ fn app() -> Element {
         button {
             class: "eval-button",
             onclick: move |_| async move {
+                // Make sure normal return values work. Regression test for https://github.com/DioxusLabs/dioxus/issues/3655
+                let eval = document::eval(r#"return "hello world";"#);
+
+                let result = eval.await.unwrap();
+                assert_eq!(result, "hello world");
+
+                // Make sure dioxus.send/dioxus.recv works
                 let mut eval = document::eval(
                     r#"
                         window.document.title = 'Hello from Dioxus Eval!';