JS::Object.await - @ledsun blog に対してフォローをもらいました。
すごーく遅レスですが、最近追加したメソッドまで追っていただいてありがとうございます!
— kateinoigakukun (@kateinoigakukun) November 30, 2022
コメントが大変紛らわしい書き方だったんですが、Ruby側でJSのPromiseを待てるもの、という認識で合っています。1/2
なんかちょっと上手く使えていないようです。 https://github.com/ruby/ruby.wasm/blob/2ef290bca9df55a9c7c64e9c03b8d4dec54f7b44/ext/js/lib/js.rb#L97-L121 のコメントを参考に、小さいコードで試してみます。
JavaScript
まずはJavaScriptにて
<html> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/browser.script.iife.js"></script> <script> function slowFunction() { return new Promise((resolve, reject) => { setTimeout(() => { resolve(123) }, 1000) }) } async function main() { const response = window.rubyVM.evalAsync(` puts "step 1" JS.global.slowFunction().await puts "step 3" `) console.log("step 2") await response console.log("step 4") } const id = setInterval(() => { if (window.rubyVM) { clearInterval(id) main() } }, 300) </script> </html>
実行するとちゃんと、1, 2, 3, 4の順で動きます。
とくに4が最後に動くのが印象的です。
await response
をすると、Promiseを待っているRubyスクリプトの完了を待てるようです。
Ruby
<html> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/browser.script.iife.js"></script> <script type="text/ruby"> require 'js' async_func = <<-"JS" return new Promise((ok) => { setTimeout(() => { console.log('step 3') ok(42) }, 1) }) JS puts 'step 1' promise = JS.eval(async_func) puts 'step 2' promise.await puts 'step 4' </script> </html>
これは 1, 2, 4, 3の順で動きます。
Promiseの終了を待っていません。
現在の<script type="text/ruby">
は自動的にrubyVM.eval
でRubyスクリプトを実行します。
もしかしてRubyスクリプトをrubyVM.evalAsync
で実行しないといけないのでしょうか?
<html> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/browser.script.iife.js"></script> <script> function slowFunction() { return new Promise((ok) => { setTimeout(() => { console.log('step 3') ok(42) }, 1) }) } async function main() { const response = window.rubyVM.evalAsync(` puts "step 1" puts 'step 2' JS.global.slowFunction().await puts "step 4" `) } const id = setInterval(() => { if (window.rubyVM) { clearInterval(id) main() } }, 300) </script> </html>
これは 1, 2, 3, 4の順で動きます。 なるほどー。 そらそうですね。 最初の呼び出しを非同期関数にしなかったら、非同期関数を待てるわけないですね。 PromiseやAsync/Awaitはコールバックヘルを縦に並べ直したものです。 最初の呼び出しでコールバック関数が渡せなければ、待ちようがありません。
なるほど、そういうつもりで書き直さないといけないのですね。