質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.53%
Firefox

Mozilla Foundationによって作られた無料、オープンソース、クロスプラットフォームなウェブブラウザ

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

解決済

1回答

1115閲覧

【Rust】wasm_bindgen_futures::spawn_localを使ったコードの自動テストが一見パスしているようにみえるが処理が完了する前にプログラムが終了してしまうようなので解決したい

akira_kano1101

総合スコア25

Firefox

Mozilla Foundationによって作られた無料、オープンソース、クロスプラットフォームなウェブブラウザ

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

0クリップ

投稿2022/12/03 13:56

編集2022/12/17 15:06

前提

Rustでwasmのコードを自動テストするプログラムを作成しています。
wasm_bindgen_futuresクレートのspawn_localメソッドに関するテストを作成している中、非同期処理にてうまく出力できない問題が発生しました。

実現したいこと

  • wasm_bindgen_futures::spawn_localを使うコードの自動テストを実行できるようにする

発生している問題・エラーメッセージ

GitHubを用意しました。よろしければこちらからも確認できますので、参照お願いします。

https://github.com/kano1101/wasm_bindgen_futures_test

プロジェクトの構成

console

1% pwd 2/Users/a.kano/development/rust/github_crate/wasm_bindgen_futures_test 3% tree -L 2 . 4. 5|-- Cargo.toml 6|-- build_run.sh 7|-- build_test.sh 8|-- index.html 9|-- src 10| `-- main.rs 11`-- webdriver.json 12 131 directory, 6 files
Cargo.toml

toml

1[package] 2name = "wasm_bindgen_futures_test" 3version = "0.1.0" 4authors = ["Akira Kano <a.kano1101@gmail.com>"] 5edition = "2021" 6 7[dependencies] 8anyhow = "1.0.66" 9console_error_panic_hook = "0.1.7" 10log = "0.4.6" 11once_cell = "1.16.0" 12reqwest = { version = "0.11.13", default-features = false, features = ["json"] } 13serde = { version = "1.0.148", default-features = false, features = ["derive"] } 14tokio = { version = "1.22.0", default-features = false, features = ["sync"] } 15wasm-bindgen-futures = "0.4.33" 16wasm-bindgen-test = "0.3.33" 17wasm-logger = "0.2.0"
build_run.sh

bash

1( 2 cd `dirname $0` 3 cargo build --target wasm32-unknown-unknown 4 wasm-bindgen ./target/wasm32-unknown-unknown/debug/$(basename `pwd`).wasm --out-dir . --web 5 python -m http.server 8080 6)
build_test.sh

bash

1( 2 cd `dirname $0` 3 wasm-pack test --headless --firefox 4)
webdriver.json

json

1{ 2 "moz:firefoxOptions": { 3 "prefs": { 4 "media.navigator.streams.fake": true, 5 "media.navigator.permission.disabled": true 6 }, 7 "args": [] 8 } 9}
index.html

html

1<!DOCTYPE html> 2<html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>Document</title> 8 </head> 9 <body> 10 <h1>Testing now...</h1> 11 <script type="module"> 12 import init from "./wasm_bindgen_futures_test.js"; 13 14 init('./wasm_bindgen_futures_test_bg.wasm'); 15 </script> 16 </body> 17</html>

うまくいっているブラウザ動作について

下記コマンドを実行します。

console

1% ./build_run.sh 2 Finished dev [unoptimized + debuginfo] target(s) in 0.05s 3Serving HTTP on :: port 8080 (http://[::]:8080/) ...

その後、ブラウザでlocalhost:8080にアクセスします。

イメージ説明

上図のように、コンソールログにip.origin: XXXが表示されています。
この出力から、プログラムが正しく動作していることが確認できました。

問題となっている自動テストについて

下記コマンドを実行します。

console

1% ./build_test.sh 2[INFO]: 🎯 Checking for the Wasm target... 3 Compiling wasm_bindgen_futures_test v0.1.0 (/Users/a.kano/development/rust/github_crate/wasm_bindgen_futures_test) 4 Finished dev [unoptimized + debuginfo] target(s) in 0.99s 5 Finished test [unoptimized + debuginfo] target(s) in 0.04s 6 Running unittests src/main.rs (target/wasm32-unknown-unknown/debug/deps/wasm_bindgen_futures_test-bd660ad6d6ccf035.wasm) 7Set timeout to 20 seconds... 8Running headless tests in Firefox on `http://127.0.0.1:55679/` 9Try find `webdriver.json` for configure browser's capabilities: 10Ok 11running 1 test 12 13test wasm_bindgen_futures_test::first_test ... ok 14 15test result: ok. 1 passed; 0 failed; 0 ignored 16console.log div contained: 17 %cDEBUG%c src/main.rs:101%c some debug log 18 color: white; padding: 0 3px; background: blue; 19 font-weight: bold; color: inherit 20 background: inherit; color: inherit 21 22console.log div contained: 23 %cERROR%c src/main.rs:104%c some error log 24 color: white; padding: 0 3px; background: darkred; 25 font-weight: bold; color: inherit 26 background: inherit; color: inherit

上記から、一見すると自動テスト自体はパスしているように見えます。
ただ、よく見るとどうやら目的とする処理に辿り着く前にプログラムが終了してしまっているようなのです。
なぜならうまくテストが実行されていれば、上記のログにip.origin: XXXが出力されているはずです。
しかし、該当する出力は確認できないため、自動テストが意味を成していないことがわかります。
これが、今回の問題です!
私は、この非同期の終了を待ちたいです。どうすれば待つことができるでしょうか?

該当のソースコード

main.rs

rust

1use serde::Deserialize; 2#[derive(Deserialize)] 3struct Ip { 4 origin: String, 5} 6 7mod space { 8 use once_cell::sync::Lazy; 9 use tokio::sync::Mutex; 10 11 static INSTANCE: Lazy<Mutex<Option<String>>> = Lazy::new(|| Mutex::new(None)); 12 13 pub async fn pull() -> anyhow::Result<()> { 14 use super::Ip; 15 let response = reqwest::get("http://httpbin.org/ip") 16 .await? 17 .json::<Ip>() 18 .await?; 19 let mut lock = INSTANCE.lock().await; 20 *lock = Some(response.origin); 21 Ok(()) 22 } 23 pub async fn get() -> Option<String> { 24 let lock = INSTANCE.lock().await; 25 lock.clone() 26 } 27} 28 29fn run() { 30 console_error_panic_hook::set_once(); 31 wasm_logger::init(wasm_logger::Config::default()); 32 33 log::trace!("some trace log"); 34 log::debug!("some debug log"); 35 log::info!("some info log"); 36 log::warn!("some warn log"); 37 log::error!("some error log"); 38 39 let fut = || async move { 40 assert!(space::get().await.is_none()); 41 assert!(space::pull().await.is_ok()); 42 let origin = space::get().await; 43 assert!(origin.is_some()); 44 log::debug!("ip.origin: {:?}", origin); 45 46 // 当然ですがコメントアウトを外すと、localhost:8080からの実行ではエラーになる 47 // しかし自動テストの方ではエラーにならない(処理が到達していないことがわかる) 48 // assert!(false); 49 }; 50 51 wasm_bindgen_futures::spawn_local(fut()); 52} 53 54fn main() { 55 run(); 56} 57 58use wasm_bindgen_test::wasm_bindgen_test; 59use wasm_bindgen_test::wasm_bindgen_test_configure; 60wasm_bindgen_test_configure!(run_in_browser); 61 62#[wasm_bindgen_test] 63async fn first_test() { 64 run(); 65}

補足情報(FW/ツールのバージョンなど)

console

1% zsh --version 2zsh 5.8.1 (x86_64-apple-darwin22.0) 3% python --version 4Python 3.11.0 5% wasm-pack --version 6wasm-pack 0.10.3 7% wasm-bindgen --version 8wasm-bindgen 0.2.83 9% cargo --version 10cargo 1.65.0 (4bc8f24d3 2022-10-20)

ブラウザはfirefoxを使っています。

解決方法ご存知の方、いらっしゃいましたらご教示のほどよろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

spawn の名の通り待てないものだと思います。
wasm_bindgen_futuresのソースコードを読んでみましたが、 Promisethen を呼んでおり、特に待つ手段もなさそうです。
https://github.com/rustwasm/wasm-bindgen/blob/main/crates/futures/src/queue.rs#L55

もし特別に run 内で spawn_local を呼ぶ必要がないなら runasync fn にして main 内で spawn_local を呼んではどうでしょうか。

diff

1diff --git a/src/main.rs b/src/main.rs 2index 1c60e97..1199faa 100644 3--- a/src/main.rs 4+++ b/src/main.rs 5@@ -26,7 +26,7 @@ mod space { 6 } 7 } 8 9-fn run() { 10+async fn run() { 11 console_error_panic_hook::set_once(); 12 wasm_logger::init(wasm_logger::Config::default()); 13 14@@ -36,23 +36,19 @@ fn run() { 15 log::warn!("some warn log"); 16 log::error!("some error log"); 17 18- let fut = || async move { 19- assert!(space::get().await.is_none()); 20- assert!(space::pull().await.is_ok()); 21- let origin = space::get().await; 22- assert!(origin.is_some()); 23- log::debug!("ip.origin: {:?}", origin); 24+ assert!(space::get().await.is_none()); 25+ assert!(space::pull().await.is_ok()); 26+ let origin = space::get().await; 27+ assert!(origin.is_some()); 28+ log::debug!("ip.origin: {:?}", origin); 29 30- // 当然ですがコメントアウトを外すと、localhost:8080からの実行ではエラーになる 31- // しかし自動テストの方ではエラーにならない(処理が到達していないことがわかる) 32- // assert!(false); 33- }; 34- 35- wasm_bindgen_futures::spawn_local(fut()); 36+ // 当然ですがコメントアウトを外すと、localhost:8080からの実行ではエラーになる 37+ // しかし自動テストの方ではエラーにならない(処理が到達していないことがわかる) 38+ // assert!(false); 39 } 40 41 fn main() { 42- run(); 43+ wasm_bindgen_futures::spawn_local(run()); 44 } 45 46 use wasm_bindgen_test::wasm_bindgen_test; 47@@ -61,5 +57,5 @@ wasm_bindgen_test_configure!(run_in_browser); 48 49 #[wasm_bindgen_test] 50 async fn first_test() { 51- run(); 52+ run().await; 53 }

これならブラウザでもテストでも動作します。

ブラウザ
イメージ説明

テスト

wasm-pack test --headless --firefox [INFO]: Checking for the Wasm target... Finished dev [unoptimized + debuginfo] target(s) in 0.04s Finished test [unoptimized + debuginfo] target(s) in 0.04s Running unittests src/main.rs (target/wasm32-unknown-unknown/debug/deps/wasm_bindgen_futures_test-0bdd71b124af7450.wasm) Set timeout to 20 seconds... Running headless tests in Firefox on `http://127.0.0.1:41051/` Try find `webdriver.json` for configure browser's capabilities: Ok running 1 test test wasm_bindgen_futures_test::first_test ... ok test result: ok. 1 passed; 0 failed; 0 ignored console.log div contained: %cDEBUG%c src/main.rs:34%c some debug log color: white; padding: 0 3px; background: blue; font-weight: bold; color: inherit background: inherit; color: inherit %cDEBUG%c src/main.rs:43%c ip.origin: Some("124.213.81.123") color: white; padding: 0 3px; background: blue; font-weight: bold; color: inherit background: inherit; color: inherit console.log div contained: %cERROR%c src/main.rs:37%c some error log color: white; padding: 0 3px; background: darkred; font-weight: bold; color: inherit background: inherit; color: inherit

投稿2022/12/16 16:31

blackenedgold

総合スコア468

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

akira_kano1101

2022/12/17 06:06

blackenedgold様 ご回答ありがとうございます。 言われてみればそうですね。awaitをつける位置を浅くするだけですね。 確かに動作させることができたので、悩みが解決できて良かったです。 ちなみに今回の質問は、ブラウザ上のwasm環境においてeguiというGUIクレートを使うのに、reqwestクレートを使用して非同期にWebAPIの値を取得したいというような内容のものでした。 いまの答えをいただいた後に確認したのですが、eguiのバージョンが(0.20.0に)上がりstart_webを動作させる際に、`wasm_bindgen_futures::spawn_local`で包んで使用されることが公式になったようですね。 https://github.com/emilk/eframe_template/commit/0d7b06b16088ce16b3b027a97541a69ea40a972c (0.19.0の時点ではこれにラップされておらず、この解法に気づくことができませんでした。) ご回答くださった内容と、eguiがバージョンアップされたことで非同期処理について理解ができました。 助かりました。また機会があればよろしくお願いします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.53%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問