-
Notifications
You must be signed in to change notification settings - Fork 177
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Question] Are there any mechanisms to pass a result of Promise into WASM(Rust) code? And how do you use it? #138
Comments
stdweb has [dependencies]
futures = "0.1"
[dependencies.stdweb]
version = "0.4"
features = ["experimental_features_which_may_break_on_minor_version_bumps"] and use stdweb::{Promise, PromiseFuture};
use futures::Future;
let promise = Promise::from_thenable(&js! {
return new Promise(function (resolve, reject) {
// some code
})}.try_into()
.unwrap());
let future = promise.unwrap().to_future(). will work, though it's experimental (see #127). In addition, I think ES2017 is not yet supported. |
Thank you! I might write a code for Promise, but I have next trouble. Can I keep this issue as my questions? I want to pass Float32Array from Rust code to JS in js! macro statement.
Is using UnsafeTypedArray for serialize wrong? Sorry for bothering you. |
@y-ich For using Promises, this is how your code should be: let future: PromiseFuture<SomeType> = js!( return asyncFunc(@{some_value}); ).try_into().unwrap();
For example, if the JavaScript Promise resolves to a String then you should use the type Or if the JavaScript Promise resolves to an Array of Numbers, then you should use the type After that you can import the let future = future.map(|x| { ... }).and_then(|x| { ... }); And finally you can spawn the Future, which causes it to actually be run: use stdweb::PromiseFuture;
PromiseFuture::spawn(
future.map_err(|e| console!(error, e))
); You should generally only call |
@y-ich The |
@pauen san's first point is my problem! Actually I have missed return statement in js! macro. Now I got next panic in the wait method of Future... Thank you for you guys! |
@y-ich @Pauan |
@kngwyu Yes, you're correct. Because of limitations in JavaScript it is impossible to use As for your example, it's not necessary to use fn read_static_file(fname: &str) -> PromiseFuture<ArrayBuffer> {
// create promise wrapping Ajax
js! (
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.status == 200) {
console.log("got reponse");
resolve(xhr.response);
} else {
reject(new Error(xhr.statusText));
}
};
xhr.open("GET", @{fname});
xhr.responseType = "arraybuffer";
xhr.onerror = reject;
xhr.send();
});
).try_into().unwrap()
} Also, if the Promise gives an error, I recommend using let future = read_static_file(FILE_PATH)
.map(move |res| {
let txt: Vec<u8> = res.into();
println!("a.txt: {}", str::from_utf8(&txt).unwrap());
})
.map_err(|err| console!(error, err)); But other than that, your example looks good! Is it okay if we use your example in stdweb? |
@Pauan |
Umm, I am shocked because my Rust code is totally designed assuming that it gets return values from wrapper function of js!... You can not see the return value in the thread that spawns the promisefuture. |
I tried to use Channel but don't suceeded.
I got an error "no method named I am not quite sure about threads on stdweb Process and PromiseFuture. |
@y-ich
fn main() {
stdweb::initialize();
let (p, c) = oneshot::channel::<String>();
let future = read_static_file(FILE_PATH)
.map(|res| {
let txt: Vec<u8> = res.into();
let s = str::from_utf8(&txt).unwrap();
p.send(s.to_owned()).expect("couldn't send string!");
})
.map_err(|err| console!(error, err));
PromiseFuture::spawn(future);
let _ = c.map(|s| println!("a.txt: {}", s));
stdweb::event_loop();
} |
JavaScript is single-threaded, but what you said is basically correct: JavaScript does not allow you to block the main thread, which is why This is a limitation in all JavaScript programs, so it is impossible for stdweb to fix it. So if you want to do an asynchronous operation then you must use the fn main() {
let future: PromiseFuture<SomeType> = js!( return asyncFunc(@{some_value}); ).try_into().unwrap();
PromiseFuture::spawn(
future
.map(|value| {
// We can access the future's value inside of this closure
do_something_with_value(value);
})
.map_err(|e| console!(error, e))
);
} If you need to use the result of Also, you don't need to use channels or |
Thank you for your comments. This limitation is pretty interesting, it is like Monad in Haskell. Thank you. |
@y-ich Yes! It's exactly like Monad in Haskell. JavaScript Promises, Rust Futures, and Haskell Monads are all very similar to each other. As an example, I wish you good luck with learning. If you ever have any more questions, please ask us and we will do our best to help. |
@Pauan san, thank you for your kind word! |
Just self follow-up. So I may be able to avoid explicit Monad world^^ |
@y-ich Yes, async-await is very nice, however it is currently only supported on Nightly Rust. So if you want your code to work on Beta or Stable then unfortunately you'll need to use the |
@Pauan san, thank you for your advice. Since I love box syntax, I am usually using nightly Rust^^ I have a next question. Here is a signature of my function. #[derive(Serialize)]
pub struct SomeType {
...
}
js_serializable!(SomeType);
#[js_export]
#[async]
pub fn js_call_this() -> Result<SomeType, stdweb::Error> {} and I got an error below.
I can understand that I need to serialize Result, but Result is not mine. Thanks. |
@y-ich impl JsSerializeOwned for futures::__rt::MyFuture<std::result::Result<SomeType, [type error]>> {} is needed(though I don't know we can implement it..)(and here's impl trait's document). In addition, I think it's impossible to use |
@kngwyu san, I am sorry that I am using this github issue like Stack Overflow. |
@y-ich |
@kngwyu san. |
But any closures inside of the Future can have local references, because they have their own stack. I just now tested it, and #![feature(proc_macro, conservative_impl_trait, generators)]
#[macro_use]
extern crate stdweb;
extern crate futures_await as futures;
use futures::prelude::*;
use stdweb::PromiseFuture;
use stdweb::web::error::Error;
use stdweb::unstable::TryInto;
// DO NOT USE THIS!
// This does NOT support cancellation! It is used ONLY as an example!
fn wait() -> PromiseFuture<()> {
js!(
return new Promise(function (success, error) {
setTimeout(function () {
success();
}, 1000);
});
).try_into().unwrap()
}
#[async]
fn testing() -> Result<(), Error> {
let mut foo: Vec<u32> = vec![1, 2, 3];
{
let r = &foo;
println!("{:#?}", r);
}
await!(wait())?;
foo.push(4);
{
let r = &foo;
println!("{:#?}", r);
}
await!(wait())?;
foo.push(5);
{
let r = &foo;
println!("{:#?}", r);
}
Ok(())
}
fn main() {
PromiseFuture::spawn(
testing().map_err(|e| console!(error, e))
);
} @y-ich It's true that stdweb cannot serialize Could you please give your full code in a gist, so that way I can see what the problem is? |
Oh, I had no idea to spawn |
@Pauan san, Now I have no idea to return back a value to JS since PromiseFuture::spawn has no return values. |
I succeeded to return back a value to JS by a tricky resolve function. I will apply this method to my real project anyway. Thank you for you guys' many helps! |
@y-ich Ahh, I understand now what you are trying to do. We do not currently have a way to convert from a Rust Future into a JavaScript Promise. But I am now working on adding in a @koute Is it possible to have higher-order callbacks with the let callback = move |success: FnOnce( A::Item ), error: FnOnce( A::Error )| {
...
};
js!( ... @{Once( callback )} ... ).try_into().unwrap() |
@y-ich I now created a pull request which adds in a After that pull request is accepted and after stdweb releases a new version, you will be able to do this: #![feature(proc_macro, conservative_impl_trait, generators)]
extern crate futures_await as futures;
#[macro_use]
extern crate stdweb;
use futures::prelude::*;
use stdweb::{Promise, PromiseFuture, js_export};
use stdweb::web::error::Error;
use stdweb::unstable::TryInto;
fn from_js_async() -> PromiseFuture<i32> {
js!( return js_async(); ).try_into().unwrap()
}
/// rust_async should return a Promise that returns a retun value of a Promise of js_async defined in main.js.
#[js_export]
fn rust_async() -> Promise {
Promise::from_future(from_js_async())
} Now in JavaScript you can call If you want to use the #![feature(proc_macro, conservative_impl_trait, generators)]
extern crate futures_await as futures;
#[macro_use]
extern crate stdweb;
use futures::prelude::*;
use stdweb::{Promise, PromiseFuture, js_export};
use stdweb::web::error::Error;
use stdweb::unstable::TryInto;
fn from_js_async() -> PromiseFuture<i32> {
js!( return js_async(); ).try_into().unwrap()
}
#[async]
fn use_js_async() -> Result<i32, Error> {
let a = await!(from_js_async())?;
// use the result of from_js_async in here
Ok(a)
}
/// rust_async should return a Promise that returns a retun value of a Promise of js_async defined in main.js.
#[js_export]
fn rust_async() -> Promise {
Promise::from_future(use_js_async())
} Notice that the |
@Pauan san, it is cool! |
@y-ich stdweb 0.4.2 now has support for You can use it like how I showed in my previous message. |
@Pauan san, thank you for your notification! I have another problem (alexcrichton/futures-await#11) in my app about futures-await and it seems to be resolved in next version(0.2.0-alpha). And 0.2.0-alpha futures, that future-await 0.2.0-alpha uses, and 0.1.18 futures, that stdweb uses, seem to has a conflict for derive macro though I don't understand well. So I am looking forward to the release of 0.2.0 of futures and futures-await and adoption of it by stdweb. I leave you contributers to decide to close this issue. |
Hi.
I want to pass a result of JS Promise into Rust code like the below.
I tried such a code but got an error "SyntaxError: Unexpected identifier 'asyncFunc'" when loading generated js file.
I should use rather Promise but I have no idea how I can take the result out from js! macro.
The text was updated successfully, but these errors were encountered: